Permalink
Browse files

Updated springy to latest code.

  • Loading branch information...
1 parent af82e40 commit bc489ac33e0d7a4275bc467544dcea7719054509 @dhotson committed Jun 15, 2010
Showing with 621 additions and 219 deletions.
  1. +22 −0 LICENSE
  2. +0 −7 README
  3. +89 −0 README.mkdn
  4. +27 −195 demo.html
  5. +0 −7 raphael-min.js
  6. +193 −10 springy.js
  7. +290 −0 springyui.js
View
22 LICENSE
@@ -0,0 +1,22 @@
+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
7 README
@@ -1,7 +0,0 @@
-Springy - Force directed graph layout algorithm in JavaScript
-----
-
-See demo.html for example of usage.
-
-Some proper docs coming soon..
-
View
@@ -0,0 +1,89 @@
+Springy
+====
+
+A force directed graph layout algorithm in JavaScript.
+
+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.
+
+
+Basic Usage
+----
+
+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.
+
+See demo.html and springyui.js for an example of usage.
+
+
+Advanced Usage
+----
+
+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 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 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 Renderer(10, 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.
View
222 demo.html
@@ -1,208 +1,40 @@
<html>
<body>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
-<script src="raphael-min.js"></script>
<script src="springy.js"></script>
+<script src="springyui.js"></script>
<script>
var graph = new Graph();
-
-// generate a random graph
-
-var n = [];
-for (var i=0; i<15; i += 1)
-{
- n[i] = graph.newNode({label: ""+i});
-}
-
-for (i=0; i<20; i += 1)
-{
- var i1 = Math.floor(Math.random() * n.length);
- var i2 = i1;
-
- while (i1 === i2) {
- i2 = Math.floor(Math.random() * n.length);
- }
-
- var n1 = n[i1];
- var n2 = n[i2];
-
- var e = graph.newEdge(n1, n2);
-
- var colors = ['#00A0B0', '#6A4A3C', '#CC333F', '#EB6841', '#EDC951', '#7DBE3C'];
- e.data.stroke = colors[Math.floor(Math.random() * colors.length)];
-}
-
-
-// -----------
-
-var width = 800;
-var height = 600;
-var zoom = 40.0;
-
-// convert to/from screen coordinates
-toScreen = function(p) {
- return new Vector(p.x * zoom + width/2.0, p.y * zoom + height/2.0);
-};
-
-fromScreen = function(s) {
- return new Vector((s.x - width/2.0) / zoom, (s.y - height/2.0) / zoom);
-};
-
-var paper = Raphael(10, 10, width, height);
-
-var layout = new Layout.ForceDirected(graph, 500.0, 300.0, 0.5);
-
-var boxWidth = 30;
-var boxHeight = 20;
-
-var renderer = new Renderer(1, layout,
- function clear()
- {
- paper.clear();
- var r = paper.rect(0,0,width-1,height-1);
- r.attr({"fill": "#FFFFFF", "stroke": "none"});
- },
- function drawEdge(edge, p1, p2)
- {
- var x1 = toScreen(p1).x;
- var y1 = toScreen(p1).y;
- var x2 = toScreen(p2).x;
- var y2 = toScreen(p2).y;
-
- var direction = new Vector(x2-x1, y2-y1);
- var normal = direction.normal().normalise();
-
- var from = graph.getEdges(edge.source, edge.target);
- var to = graph.getEdges(edge.target, edge.source);
-
- var total = from.length + to.length;
- var n = from.indexOf(edge);
-
- var spacing = 6.0;
- var totalWidth = total * spacing;
-
- // Figure out how far off centre the line should be drawn
- var offset = normal.multiply(-((total - 1) * spacing)/2.0 + (n * spacing));
-
- var stroke = typeof(edge.data.stroke) !== 'undefined' ? edge.data.stroke : "#000000";
-
- var s1 = toScreen(p1).add(offset);
- var s2 = toScreen(p2).add(offset);
-
- var intersection = intersect_line_box(s1, s2, {x: x2-boxWidth/2.0, y: y2-boxHeight/2.0}, boxWidth, boxHeight);
-
- var c2 = paper.path(["M", s1.x, s1.y, "L", intersection.x, intersection.y]);
- c2.attr({stroke: stroke, "stroke-width": 2});
-
- var arrow = paper.path(["M", -7, 4, "L", 0, 0, "L", 7, 0, "L", 0, 0, "L", -7, -4, "L", -5, 0, "z"]);
- arrow.rotate(Math.atan2(y2 - y1, x2 - x1) * (180.0 / Math.PI), 0, 0);
- arrow.translate(intersection.x, intersection.y);
- arrow.attr({fill: stroke, stroke: "none"});
-
- },
- function drawNode(node, p)
- {
- var fill = typeof(node.data.fill) !== 'undefined' ? node.data.fill : "#FFFFFF";
-
- var s = toScreen(p);
- var rect = paper.rect(s.x - boxWidth/2.0, s.y - boxHeight/2.0, boxWidth, boxHeight, 4);
- rect.attr({fill: fill, "stroke-width": 2});
-
- if (typeof(node.data.label) !== 'undefined')
- {
- var text = paper.text(s.x, s.y, node.data.label);
- text.attr({
- "font-family": "Helvetica Neue",
- "font-size": "12px",
- "font-weight": "bold",
- });
-
- }
- }
-);
-
-renderer.start();
-
-
-// half-assed drag and drop
-var selected = null;
-jQuery('svg').mousedown(function(e){
- var pos = jQuery(this).position();
- var p = fromScreen({x: e.pageX - pos.left, y: e.pageY - pos.top});
- selected = layout.nearest(p);
-
- selected.oldm = selected.point.m;
- selected.olddata = selected.node.data;
- selected.node.data = jQuery.extend(true, {}, selected.node.data); // deep copy
-
- selected.point.m = 1000.0;
- selected.node.data.fill = '#EEEEEE';
-
-});
-
-jQuery('svg').mousemove(function(e){
- if (selected !== null)
- {
- var pos = jQuery(this).position();
- var p = fromScreen({x: e.pageX - pos.left, y: e.pageY - pos.top});
-
- selected.point.p.x = p.x;
- selected.point.p.y = p.y;
- renderer.start();
- }
+var dennis = graph.newNode({label: 'Dennis'});
+var michael = graph.newNode({label: 'Michael'});
+var jessica = graph.newNode({label: 'Jessica'});
+var timothy = graph.newNode({label: 'Timothy'});
+var barbara = graph.newNode({label: 'Barbara'});
+var franklin = graph.newNode({label: 'Franklin'});
+var monty = graph.newNode({label: 'Monty'});
+var james = graph.newNode({label: 'James'});
+var bianca = graph.newNode({label: 'Bianca'});
+
+graph.newEdge(dennis, michael, {color: '#00A0B0'});
+graph.newEdge(michael, dennis, {color: '#6A4A3C'});
+graph.newEdge(michael, jessica, {color: '#CC333F'});
+graph.newEdge(jessica, barbara, {color: '#EB6841'});
+graph.newEdge(michael, timothy, {color: '#EDC951'});
+graph.newEdge(franklin, monty, {color: '#7DBE3C'});
+graph.newEdge(dennis, monty, {color: '#000000'});
+graph.newEdge(monty, james, {color: '#00A0B0'});
+graph.newEdge(barbara, timothy, {color: '#6A4A3C'});
+graph.newEdge(dennis, bianca, {color: '#CC333F'});
+graph.newEdge(bianca, monty, {color: '#EB6841'});
+
+jQuery(document).ready(function(){
+ jQuery('#springydemo').springy(graph);
});
-jQuery(window).bind('mouseup',function(e){
- if (selected !== null)
- {
- selected.node.data = selected.olddata;
- }
- selected = null;
-});
-
-
-// helpers for figuring out where to draw arrows
-function intersect_line_line(p1, p2, p3, p4)
-{
- var denom = ((p4.y - p3.y)*(p2.x - p1.x) - (p4.x - p3.x)*(p2.y - p1.y));
-
- // lines are parallel
- if (denom === 0) {
- return false;
- }
-
- var ua = ((p4.x - p3.x)*(p1.y - p3.y) - (p4.y - p3.y)*(p1.x - p3.x)) / denom;
- var ub = ((p2.x - p1.x)*(p1.y - p3.y) - (p2.y - p1.y)*(p1.x - p3.x)) / denom;
-
- if (ua < 0 || ua > 1 || ub < 0 || ub > 1) {
- return false;
- }
-
- return {
- x: p1.x + ua * (p2.x - p1.x),
- y: p1.y + ua * (p2.y - p1.y)
- };
-}
-
-function intersect_line_box(p1, p2, p3, w, h)
-{
- var tl = {x: p3.x, y: p3.y};
- var tr = {x: p3.x + w, y: p3.y};
- var bl = {x: p3.x, y: p3.y + h};
- var br = {x: p3.x + w, y: p3.y + h};
-
- var result;
- if (result = intersect_line_line(p1, p2, tl, tr)) { return result; } // top
- if (result = intersect_line_line(p1, p2, tr, br)) { return result; } // right
- if (result = intersect_line_line(p1, p2, br, bl)) { return result; } // bottom
- if (result = intersect_line_line(p1, p2, bl, tl)) { return result; } // left
-
- return false;
-}
-
-
</script>
+
+<canvas id="springydemo" width="640" height="480" />
</body>
</html>
View

Large diffs are not rendered by default.

Oops, something went wrong.
Oops, something went wrong.

0 comments on commit bc489ac

Please sign in to comment.