Compare commits
No commits in common. "master" and "gh-pages" have entirely different histories.
22
LICENSE
22
LICENSE
|
@ -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.
|
|
129
README.mkdn
129
README.mkdn
|
@ -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
BIN
araucaria_brasiliana.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
17
bower.json
17
bower.json
|
@ -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
BIN
chamaecyparis_obtusa.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
BIN
cupressus_sempervirens.png
Normal file
BIN
cupressus_sempervirens.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
|
@ -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
BIN
favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
309
index.html
Normal file
309
index.html
Normal 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 “force directed” 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‑decent default renderer and some half‑assed drag and drop.</p>
|
||||||
|
|
||||||
|
<p>Here's how to use springyui.js:</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<span class="Function"><</span><span class="Statement">canvas</span><span class="Function"> </span><span class="Type">id</span><span class="Function">=</span><span class="String">"my_canvas"</span><span class="Function"> </span><span class="Type">width</span><span class="Function">=</span><span class="String">"600"</span><span class="Function"> </span><span class="Type">height</span><span class="Function">=</span><span class="String">"400"</span><span class="Function"> /></span>
|
||||||
|
<span class="Function"><</span><span class="Statement">script</span><span class="Function">></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"></</span><span class="Statement">script</span><span class="Identifier">></span>
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<h3>Do your own rendering</h3>
|
||||||
|
<p>If you're keen to do your own custom drawing or interaction—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—it's a good place to start. Feel free to copy‑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—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>
|
21
package.json
21
package.json
|
@ -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
BIN
pinus_serotina.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
BIN
sequoya_gigantea.png
Normal file
BIN
sequoya_gigantea.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
12
springy.js
12
springy.js
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user