Compare commits

...

No commits in common. "master" and "gh-pages" have entirely different histories.

14 changed files with 313 additions and 199 deletions

1
CNAME Normal file
View File

@ -0,0 +1 @@
getspringy.com

22
LICENSE
View File

@ -1,22 +0,0 @@
Copyright (c) 2010 Dennis Hotson
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,129 +0,0 @@
Springy
====
A force directed graph layout algorithm in JavaScript.
[http://getspringy.com/](http://getspringy.com/)
What is this?
----
Springy is a force directed graph layout algorithm.
So what does this 'force directed' stuff mean anyway? Excellent question!
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.
Try to imagine it as a bunch of springs connected to each other.
Demo
----
[basic](http://dhotson.github.com/springy/demo.html)
| [simplified API](http://dhotson.github.com/springy/demo-simple.html)
| [JSON API](http://dhotson.github.com/springy/demo-json.html)
Getting Started
----
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.
The drawing and interaction stuff is mostly up to you.
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.
Basic Usage
----
See [demo.html](http://dhotson.github.com/springy/demo.html) for the way to
add nodes and edges to graph and springyui.js for the rendering example.
Springy 1.1+ supports simplified API for adding nodes and edges, see
[demo-simple.html](http://dhotson.github.com/springy/demo-simple.html):
var graph = new Springy.Graph();
graph.addNodes('mark', 'higgs', 'other', 'etc');
graph.addEdges(
['mark', 'higgs'],
['mark', 'etc'],
['mark', 'other']
);
Springy 1.2+ also accepts JSON, see
[demo-json.html](http://dhotson.github.com/springy/demo-json.html):
graphJSON = {
"nodes": ["mark", "higgs", "other", "etc"],
"edges": [
["mark", "higgs"],
["mark", "etc"],
["mark", "other"]
]
};
var graph = new Springy.Graph();
graph.loadJSON(graphJSON);
Advanced Drawing
----
If you're keen to do your own custom drawing, you'll need to know a few
things before you get started.
This is the basic graph API, you can create nodes and edges etc.
// make a new graph
var graph = new Springy.Graph();
// make some nodes
var node1 = graph.newNode({label: '1'});
var node2 = graph.newNode({label: '2'});
// connect them with an edge
graph.newEdge(node1, node2);
So now to draw this graph, lets make a layout object:
var layout = new Springy.Layout.ForceDirected(graph, 400.0, 400.0, 0.5);
I've written a Renderer class, which will handle the rendering loop.
You just need to provide some callbacks to do the actual drawing.
var renderer = new Springy.Renderer(layout,
function clear() {
// code to clear screen
},
function drawEdge(edge, p1, p2) {
// draw an edge
},
function drawNode(node, p) {
// draw a node
}
);
Now, just start the rendering loop:
renderer.start();
Further Reading
----
Have a look at the code in springy.js.
Seriously, it's not very much code and it should be pretty easy to understand.
Please let me know if anything is unclear. Feedback is welcome.
Acknowledgements
----
Thanks to [Lachlan Donald](http://github.com/lox) for his helpful suggestions and
feedback.

BIN
araucaria_brasiliana.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -1,17 +0,0 @@
{
"name": "Springy",
"main": "springy.js",
"version": "2.7.1",
"homepage": "https://github.com/dhotson/springy",
"authors": [
"Dennis Hotson <dennis@99designs.com>"
],
"description": "A force directed graph layout algorithm",
"keywords": [
"graph",
"layout",
"visualization",
"physics"
],
"license": "MIT"
}

BIN
chamaecyparis_obtusa.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
cupressus_sempervirens.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -10,6 +10,7 @@ var dennis = graph.newNode({
label: 'Dennis', label: 'Dennis',
ondoubleclick: function() { console.log("Hello!"); } ondoubleclick: function() { console.log("Hello!"); }
}); });
var michael = graph.newNode({label: 'Michael'}); var michael = graph.newNode({label: 'Michael'});
var jessica = graph.newNode({label: 'Jessica'}); var jessica = graph.newNode({label: 'Jessica'});
var timothy = graph.newNode({label: 'Timothy'}); var timothy = graph.newNode({label: 'Timothy'});

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

309
index.html Normal file
View File

@ -0,0 +1,309 @@
<!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>
<link rel="shortcut icon" href="favicon.ico" />
<style>
* {
margin: 0;
padding: 0;
}
body {
background: #FFF;
font-family: Palatino, 'Palatino Linotype', 'Hoefler Text', Times, 'Times New Roman', serif;
font-size: 18px;
color: #000;
line-height: 24px;
text-align: center;
margin-bottom: 120px;
}
p {
margin-bottom: 24px;
}
a:link { color: #A00; }
a:visited { color: #644; }
h1 {
font-family: 'IM Fell English SC', serif;
font-weight: normal;
font-size: 48px;
line-height: 48px;
margin-top: 48px;
}
.subtitle {
font-family: 'IM Fell English', serif;
font-weight: normal;
font-size: 24px;
line-height: 48px;
font-style: italic;
}
h2 {
font-weight: normal;
font-size: 32px;
font-family: 'IM Fell English SC', serif;
line-height: 48px;
margin-top: 0;
}
h3 {
font-size: 18px;
line-height: 24px;
}
.section {
width: 650px;
margin: 0 auto;
text-align: left;
}
.divider {
margin: 48px 0;
text-align: center;
line-height: 0;
font-size: 0;
}
.example {
box-sizing: border-box;
background: #EEEEEE;
box-shadow: 0 0 50px 0 rgba(0,0,0,0.1) inset;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
}
pre {
font-family: Courier, monospace;
background: #EEEEEE;
box-shadow: 0 0 50px 0 rgba(0,0,0,0.1) inset;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
padding: 24px;
margin: 24px 0;
}
.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 in JavaScript.</p>
<div class="divider"><img src="chamaecyparis_obtusa.png" width="185" height="137" alt="Chamaecyparis obtusa" title="Chamaecyparis obtusa" /></div>
<p>
<a href="demo.html">Demo</a> |
<a href="https://github.com/dhotson/springy/zipball/master">Download</a> |
<a href="#getting-started">Getting started</a> |
<a href="https://github.com/dhotson/springy/">GitHub</a> |
<a href="mailto:dennis.hotson@gmail.com">Contact</a>
</p>
<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 &ldquo;force directed&rdquo; stuff mean anyway? <i>Excellent question!</i></p>
<p>It means that springy uses some real world physics to try and figure out how to show a network graph in a way that looks good.</p>
<p>Here's an example:</p>
<canvas id="demo" class="example" width="650" height="350"></canvas>
</div>
<!-- Here's a Springy worked example right here in the source. Enjoy! :-) -->
<script>
(function() {
// make a new graph
var graph = new Springy.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'});
var node5 = graph.newNode({label: 'Giant Sequoia'});
// connect them with an edge
graph.newEdge(node1, node2);
graph.newEdge(node2, node3);
graph.newEdge(node2, node4);
graph.newEdge(node1, node4);
graph.newEdge(node1, node5);
var layout = new Springy.Layout.ForceDirected(
graph,
300.0, // Spring stiffness
400.0, // Node repulsion
0.5 // Damping
);
var canvas = document.getElementById('demo');
var ctx = canvas.getContext('2d');
var renderer = new Springy.Renderer(layout,
function clear() {
ctx.clearRect(0, 0, 650, 350);
},
function drawEdge(edge, p1, p2) {
ctx.save();
ctx.translate(650/2, 350/2);
ctx.strokeStyle = 'rgba(0,0,0,0.15)';
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(650/2, 350/2);
ctx.font = "18px 'IM Fell English', 'Times New Roman', 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 - 2, y - 10, width + 4, 20);
ctx.fillStyle = '#000000';
ctx.fillText(node.data.label, x - width / 2.0, y + 5);
ctx.restore();
}
);
renderer.start();
})()
</script>
<div class="divider"><img src="sequoya_gigantea.png" width="146" height="108" alt="Sequoya gigantea" title="Sequoya gigantea" /></div>
<div id="getting-started" class="section">
<h2>Getting started</h2>
<p>Springy.js is designed to be small and simple. It provides an abstraction for graph manipulation and for calculating the layout and not too much else.</p>
<p>The details of drawing and interaction are mostly up to you. This means you can use canvas, SVG, WebGL, or even just plain old positioned HTML elements.</p>
<h3>Creating a graph</h3>
<p>Here's how to create a graph and add nodes and edges:</p>
<pre>
<span class="Comment">// make a new graph</span>
<span class="Identifier">var</span><span class="Special"> graph = </span><span class="Normal">new</span><span class="Special"> Springy.Graph</span>()<span class="Special">;</span>
<span class="Comment">// make some nodes</span>
<span class="Identifier">var</span><span class="Special"> spruce = graph.newNode</span>(<span class="Function">{</span><span class="Special">label: </span><span class="String">'Norway Spruce'</span><span class="Function">}</span>)<span class="Special">;</span>
<span class="Identifier">var</span><span class="Special"> fir = graph.newNode</span>(<span class="Function">{</span><span class="Special">label: </span><span class="String">'Sicilian Fir'</span><span class="Function">}</span>)<span class="Special">;</span>
<span class="Comment">// connect them with an edge</span>
<span class="Special">graph.newEdge</span>(<span class="Special">spruce, fir</span>)<span class="Special">;</span>
</pre>
<p>Once you've created a graph, there are a couple of options for displaying it.</p>
<h3>Use the springyui.js renderer</h3>
<p>To help get started quickly, I've included a helper jQuery plugin called springyui.js. It's got a semi&#8209;decent default renderer and some half&#8209;assed drag and drop.</p>
<p>Here's how to use springyui.js:</p>
<pre>
<span class="Function">&lt;</span><span class="Statement">canvas</span><span class="Function"> </span><span class="Type">id</span><span class="Function">=</span><span class="String">&quot;my_canvas&quot;</span><span class="Function"> </span><span class="Type">width</span><span class="Function">=</span><span class="String">&quot;600&quot;</span><span class="Function"> </span><span class="Type">height</span><span class="Function">=</span><span class="String">&quot;400&quot;</span><span class="Function"> /&gt;</span>
<span class="Function">&lt;</span><span class="Statement">script</span><span class="Function">&gt;</span>
<span class="Special">$</span>(<span class="String">'#my_canvas'</span>)<span class="Special">.springy</span>(<span class="Function">{</span><span class="Special"> graph: graph </span><span class="Function">}</span>)<span class="Special">;</span>
<span class="Identifier">&lt;/</span><span class="Statement">script</span><span class="Identifier">&gt;</span>
</pre>
<h3>Do your own rendering</h3>
<p>If you're keen to do your own custom drawing or interaction&mdash;there's a few extra things you'll need to know.</p>
<p>The core Springy layout algorithm is in the Springy.Layout.ForceDirected class.</p>
<p>When creating a layout object, there are a few parameters you can tune to make the graph layout algorithm behave how you like:</p>
<pre>
<span class="Identifier">var</span> layout = <span class="Normal">new</span> Springy.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>
To simplify the layout calculation and animation rendering loop, I've provided a Renderer class.
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> Springy.Renderer(
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>Then to start the rendering loop:</p>
<pre>
renderer.start();
</pre>
<p><b>Protip:</b> Take a look at the source code of <a href="https://github.com/dhotson/springy/blob/master/springyui.js">springyui.js</a> to get an idea of how to write your own renderer&mdash;it's a good place to start. Feel free to copy&#8209;paste what you need.</p>
</div>
<div class="divider"><img src="pinus_serotina.png" width="140" height="119" alt="Pinus serotina" title="Pinus serotina" /></div>
<div id="further-reading" class="section">
<h2>Further reading</h2>
<p>Take a look at the source code of <a href="https://github.com/dhotson/springy/blob/master/springy.js">springy.js</a>. Don't be shy&mdash;there's not that much code and it should be pretty easy to understand.</p>
<p>Please let me know if anything is unclear. <a href="mailto:dennis.hotson@gmail.com">Feedback is welcome</a>. <b>:-)</b></p>
</div>
<div class="divider"><img src="cupressus_sempervirens.png" width="167" height="185" alt="Cupressus sempervirens" title="Cupressus sempervirens" /></div>
<div id="license" class="section">
<h2>License</h2>
<p>Springy is licensed under the <a href="https://github.com/dhotson/springy/blob/master/LICENSE">MIT</a> license.</p>
</div>
<div id="contributions" class="section">
<h2>Contributing</h2>
<p>Contributions are welcome and highly encouraged. Please submit a pull request for fixes, features or enhancements.</p>
</div>
<div id="acknowledgements" class="section">
<h2>Acknowledgements</h2>
<p>Thanks to <a href="http://lachlan.me/">Lachlan Donald</a> for his helpful suggestions and feedback.</p>
<p>Thanks to <a href="http://en.wikipedia.org/wiki/Ernst_Haeckel">Ernst Haeckel</a> for the <a href="http://www.flickr.com/photos/origomi/sets/72157601323433758/">beautiful illustrations</a> of various <a href="http://en.wikipedia.org/wiki/Pinophyta">Coniferae</a>.</p>
</div>
<div class="divider"><img src="araucaria_brasiliana.png" width="195" height="259" alt="Araucaria brasiliana" title="Araucaria brasiliana" /></div>
<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>
</body>

View File

@ -1,21 +0,0 @@
{
"name": "springy",
"version": "2.7.1",
"description": "A force directed graph layout algorithm in JavaScript.",
"main": "springy.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git://github.com/dhotson/springy.git"
},
"keywords": [
"graph",
"layout",
"visualization"
],
"author": "Dennis Hotson <dennis.hotson@gmail.com>",
"license": "MIT",
"readmeFilename": "README.mkdn"
}

BIN
pinus_serotina.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
sequoya_gigantea.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -327,13 +327,12 @@
// ----------- // -----------
var Layout = Springy.Layout = {}; var Layout = Springy.Layout = {};
Layout.ForceDirected = function(graph, stiffness, repulsion, damping, minEnergyThreshold, maxSpeed) { Layout.ForceDirected = function(graph, stiffness, repulsion, damping, minEnergyThreshold) {
this.graph = graph; this.graph = graph;
this.stiffness = stiffness; // spring stiffness constant this.stiffness = stiffness; // spring stiffness constant
this.repulsion = repulsion; // repulsion constant this.repulsion = repulsion; // repulsion constant
this.damping = damping; // velocity damping factor this.damping = damping; // velocity damping factor
this.minEnergyThreshold = minEnergyThreshold || 0.01; //threshold used to determine render stop this.minEnergyThreshold = minEnergyThreshold || 0.01; //threshold used to determine render stop
this.maxSpeed = maxSpeed || Infinity; // nodes aren't allowed to exceed this speed
this.nodePoints = {}; // keep track of points associated with nodes this.nodePoints = {}; // keep track of points associated with nodes
this.edgeSprings = {}; // keep track of springs associated with edges this.edgeSprings = {}; // keep track of springs associated with edges
@ -452,9 +451,6 @@
// Is this, along with updatePosition below, the only places that your // Is this, along with updatePosition below, the only places that your
// integration code exist? // integration code exist?
point.v = point.v.add(point.a.multiply(timestep)).multiply(this.damping); point.v = point.v.add(point.a.multiply(timestep)).multiply(this.damping);
if (point.v.magnitude() > this.maxSpeed) {
point.v = point.v.normalise().multiply(this.maxSpeed);
}
point.a = new Vector(0,0); point.a = new Vector(0,0);
}); });
}; };
@ -645,16 +641,14 @@
* Renderer handles the layout rendering loop * Renderer handles the layout rendering loop
* @param onRenderStop optional callback function that gets executed whenever rendering stops. * @param onRenderStop optional callback function that gets executed whenever rendering stops.
* @param onRenderStart optional callback function that gets executed whenever rendering starts. * @param onRenderStart optional callback function that gets executed whenever rendering starts.
* @param onRenderFrame optional callback function that gets executed after each frame is rendered.
*/ */
var Renderer = Springy.Renderer = function(layout, clear, drawEdge, drawNode, onRenderStop, onRenderStart, onRenderFrame) { var Renderer = Springy.Renderer = function(layout, clear, drawEdge, drawNode, onRenderStop, onRenderStart) {
this.layout = layout; this.layout = layout;
this.clear = clear; this.clear = clear;
this.drawEdge = drawEdge; this.drawEdge = drawEdge;
this.drawNode = drawNode; this.drawNode = drawNode;
this.onRenderStop = onRenderStop; this.onRenderStop = onRenderStop;
this.onRenderStart = onRenderStart; this.onRenderStart = onRenderStart;
this.onRenderFrame = onRenderFrame;
this.layout.graph.addGraphListener(this); this.layout.graph.addGraphListener(this);
} }
@ -685,8 +679,6 @@
t.layout.eachNode(function(node, point) { t.layout.eachNode(function(node, point) {
t.drawNode(node, point.p); t.drawNode(node, point.p);
}); });
if (t.onRenderFrame !== undefined) { t.onRenderFrame(); }
}, this.onRenderStop, this.onRenderStart); }, this.onRenderStop, this.onRenderStart);
}; };