Update. Add demo2.html with simplied syntax of adding nodes.
This commit is contained in:
parent
e6fc4ef100
commit
1f5b8ab9ca
43
demo2.html
Normal file
43
demo2.html
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
|
||||||
|
<script src="springy.js"></script>
|
||||||
|
<script src="springyui.js"></script>
|
||||||
|
<script>
|
||||||
|
var graph = new Graph();
|
||||||
|
graph.addNodes(['Dennis', 'Michael', 'Jessica', 'Timothy', 'Barbara', 'Franklin'])
|
||||||
|
graph.addNodes(['Monty', 'James', 'Bianca']);
|
||||||
|
|
||||||
|
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', label: 'Foo bar'});
|
||||||
|
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(function(){
|
||||||
|
var springy = jQuery('#springydemo').springy({
|
||||||
|
graph: graph
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<canvas id="springydemo" width="640" height="480" />
|
||||||
|
</body>
|
||||||
|
</html>
|
116
springy.js
116
springy.js
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* Springy v1.0.1
|
* Springy v1.1.0
|
||||||
*
|
*
|
||||||
* Copyright (c) 2010 Dennis Hotson
|
* Copyright (c) 2010 Dennis Hotson
|
||||||
*
|
*
|
||||||
|
@ -25,33 +25,6 @@
|
||||||
* OTHER DEALINGS IN THE SOFTWARE.
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach
|
|
||||||
if ( !Array.prototype.forEach ) {
|
|
||||||
Array.prototype.forEach = function( callback, thisArg ) {
|
|
||||||
var T, k;
|
|
||||||
if ( this == null ) {
|
|
||||||
throw new TypeError( " this is null or not defined" );
|
|
||||||
}
|
|
||||||
var O = Object(this);
|
|
||||||
var len = O.length >>> 0; // Hack to convert O.length to a UInt32
|
|
||||||
if ( {}.toString.call(callback) != "[object Function]" ) {
|
|
||||||
throw new TypeError( callback + " is not a function" );
|
|
||||||
}
|
|
||||||
if ( thisArg ) {
|
|
||||||
T = thisArg;
|
|
||||||
}
|
|
||||||
k = 0;
|
|
||||||
while( k < len ) {
|
|
||||||
var kValue;
|
|
||||||
if ( k in O ) {
|
|
||||||
kValue = O[ k ];
|
|
||||||
callback.call( T, kValue, k, O );
|
|
||||||
}
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var Graph = function() {
|
var Graph = function() {
|
||||||
this.nodeSet = {};
|
this.nodeSet = {};
|
||||||
this.nodes = [];
|
this.nodes = [];
|
||||||
|
@ -65,18 +38,28 @@ var Graph = function() {
|
||||||
|
|
||||||
var Node = function(id, data) {
|
var Node = function(id, data) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.data = typeof(data) !== 'undefined' ? data : {};
|
this.data = (data !== undefined) ? data : {};
|
||||||
|
|
||||||
|
// Data fields used by layout algorithm in this file:
|
||||||
|
// this.data.mass
|
||||||
|
// Data used by default renderer in springyui.js
|
||||||
|
// this.data.label
|
||||||
};
|
};
|
||||||
|
|
||||||
var Edge = function(id, source, target, data) {
|
var Edge = function(id, source, target, data) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
/** @type {Node} */
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.target = target;
|
this.target = target;
|
||||||
this.data = typeof(data) !== 'undefined' ? data : {};
|
this.data = (data !== undefined) ? data : {};
|
||||||
|
|
||||||
|
// Edge data field used by layout alorithm
|
||||||
|
// this.data.length
|
||||||
|
// this.data.type
|
||||||
};
|
};
|
||||||
|
|
||||||
Graph.prototype.addNode = function(node) {
|
Graph.prototype.addNode = function(node) {
|
||||||
if (typeof(this.nodeSet[node.id]) === 'undefined') {
|
if (!(node.id in this.nodeSet)) {
|
||||||
this.nodes.push(node);
|
this.nodes.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,6 +69,15 @@ Graph.prototype.addNode = function(node) {
|
||||||
return node;
|
return node;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Graph.prototype.addNodes = function(list) {
|
||||||
|
if (typeof(list[0]) == "string") {
|
||||||
|
list.forEach(function(name) {
|
||||||
|
var node = new Node(name, data = {label:name});
|
||||||
|
this.addNode(node);
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Graph.prototype.addEdge = function(edge) {
|
Graph.prototype.addEdge = function(edge) {
|
||||||
var exists = false;
|
var exists = false;
|
||||||
this.edges.forEach(function(e) {
|
this.edges.forEach(function(e) {
|
||||||
|
@ -96,10 +88,10 @@ Graph.prototype.addEdge = function(edge) {
|
||||||
this.edges.push(edge);
|
this.edges.push(edge);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof(this.adjacency[edge.source.id]) === 'undefined') {
|
if (!(edge.source.id in this.adjacency)) {
|
||||||
this.adjacency[edge.source.id] = {};
|
this.adjacency[edge.source.id] = {};
|
||||||
}
|
}
|
||||||
if (typeof(this.adjacency[edge.source.id][edge.target.id]) === 'undefined') {
|
if (!(edge.target.id in this.adjacency[edge.source.id])) {
|
||||||
this.adjacency[edge.source.id][edge.target.id] = [];
|
this.adjacency[edge.source.id][edge.target.id] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,8 +122,8 @@ Graph.prototype.newEdge = function(source, target, data) {
|
||||||
|
|
||||||
// find the edges from node1 to node2
|
// find the edges from node1 to node2
|
||||||
Graph.prototype.getEdges = function(node1, node2) {
|
Graph.prototype.getEdges = function(node1, node2) {
|
||||||
if (typeof(this.adjacency[node1.id]) !== 'undefined'
|
if (node1.id in this.adjacency
|
||||||
&& typeof(this.adjacency[node1.id][node2.id]) !== 'undefined') {
|
&& node2.id in this.adjacency[node1.id]) {
|
||||||
return this.adjacency[node1.id][node2.id];
|
return this.adjacency[node1.id][node2.id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +132,7 @@ Graph.prototype.getEdges = function(node1, node2) {
|
||||||
|
|
||||||
// remove a node and it's associated edges from the graph
|
// remove a node and it's associated edges from the graph
|
||||||
Graph.prototype.removeNode = function(node) {
|
Graph.prototype.removeNode = function(node) {
|
||||||
if (typeof(this.nodeSet[node.id]) !== 'undefined') {
|
if (node.id in this.nodeSet) {
|
||||||
delete this.nodeSet[node.id];
|
delete this.nodeSet[node.id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,8 +255,8 @@ Layout.ForceDirected = function(graph, stiffness, repulsion, damping) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Layout.ForceDirected.prototype.point = function(node) {
|
Layout.ForceDirected.prototype.point = function(node) {
|
||||||
if (typeof(this.nodePoints[node.id]) === 'undefined') {
|
if (!(node.id in this.nodePoints)) {
|
||||||
var mass = typeof(node.data.mass) !== 'undefined' ? node.data.mass : 1.0;
|
var mass = (node.data.mass !== undefined) ? node.data.mass : 1.0;
|
||||||
this.nodePoints[node.id] = new Layout.ForceDirected.Point(Vector.random(), mass);
|
this.nodePoints[node.id] = new Layout.ForceDirected.Point(Vector.random(), mass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,14 +264,14 @@ Layout.ForceDirected.prototype.point = function(node) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Layout.ForceDirected.prototype.spring = function(edge) {
|
Layout.ForceDirected.prototype.spring = function(edge) {
|
||||||
if (typeof(this.edgeSprings[edge.id]) === 'undefined') {
|
if (!(edge.id in this.edgeSprings)) {
|
||||||
var length = typeof(edge.data.length) !== 'undefined' ? edge.data.length : 1.0;
|
var length = (edge.data.length !== undefined) ? edge.data.length : 1.0;
|
||||||
|
|
||||||
var existingSpring = false;
|
var existingSpring = false;
|
||||||
|
|
||||||
var from = this.graph.getEdges(edge.source, edge.target);
|
var from = this.graph.getEdges(edge.source, edge.target);
|
||||||
from.forEach(function(e) {
|
from.forEach(function(e) {
|
||||||
if (existingSpring === false && typeof(this.edgeSprings[e.id]) !== 'undefined') {
|
if (existingSpring === false && e.id in this.edgeSprings) {
|
||||||
existingSpring = this.edgeSprings[e.id];
|
existingSpring = this.edgeSprings[e.id];
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
|
@ -290,7 +282,7 @@ Layout.ForceDirected.prototype.spring = function(edge) {
|
||||||
|
|
||||||
var to = this.graph.getEdges(edge.target, edge.source);
|
var to = this.graph.getEdges(edge.target, edge.source);
|
||||||
from.forEach(function(e){
|
from.forEach(function(e){
|
||||||
if (existingSpring === false && typeof(this.edgeSprings[e.id]) !== 'undefined') {
|
if (existingSpring === false && e.id in this.edgeSprings) {
|
||||||
existingSpring = this.edgeSprings[e.id];
|
existingSpring = this.edgeSprings[e.id];
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
|
@ -411,7 +403,7 @@ Layout.requestAnimationFrame = __bind(window.requestAnimationFrame ||
|
||||||
|
|
||||||
|
|
||||||
// start simulation
|
// start simulation
|
||||||
Layout.ForceDirected.prototype.start = function(interval, render, done) {
|
Layout.ForceDirected.prototype.start = function(render, done) {
|
||||||
var t = this;
|
var t = this;
|
||||||
|
|
||||||
if (this._started) return;
|
if (this._started) return;
|
||||||
|
@ -424,13 +416,14 @@ Layout.ForceDirected.prototype.start = function(interval, render, done) {
|
||||||
t.updateVelocity(0.03);
|
t.updateVelocity(0.03);
|
||||||
t.updatePosition(0.03);
|
t.updatePosition(0.03);
|
||||||
|
|
||||||
if (typeof(render) !== 'undefined')
|
if (render !== undefined) {
|
||||||
render();
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
// stop simulation when energy of the system goes below a threshold
|
// stop simulation when energy of the system goes below a threshold
|
||||||
if (t.totalEnergy() < 0.01) {
|
if (t.totalEnergy() < 0.01) {
|
||||||
t._started = false;
|
t._started = false;
|
||||||
if (typeof(done) !== 'undefined') { done(); }
|
if (done !== undefined) { done(); }
|
||||||
} else {
|
} else {
|
||||||
Layout.requestAnimationFrame(step);
|
Layout.requestAnimationFrame(step);
|
||||||
}
|
}
|
||||||
|
@ -547,8 +540,7 @@ Layout.ForceDirected.Spring = function(point1, point2, length, k) {
|
||||||
// };
|
// };
|
||||||
|
|
||||||
// Renderer handles the layout rendering loop
|
// Renderer handles the layout rendering loop
|
||||||
function Renderer(interval, layout, clear, drawEdge, drawNode) {
|
function Renderer(layout, clear, drawEdge, drawNode) {
|
||||||
this.interval = interval;
|
|
||||||
this.layout = layout;
|
this.layout = layout;
|
||||||
this.clear = clear;
|
this.clear = clear;
|
||||||
this.drawEdge = drawEdge;
|
this.drawEdge = drawEdge;
|
||||||
|
@ -563,7 +555,7 @@ Renderer.prototype.graphChanged = function(e) {
|
||||||
|
|
||||||
Renderer.prototype.start = function() {
|
Renderer.prototype.start = function() {
|
||||||
var t = this;
|
var t = this;
|
||||||
this.layout.start(50, function render() {
|
this.layout.start(function render() {
|
||||||
t.clear();
|
t.clear();
|
||||||
|
|
||||||
t.layout.eachEdge(function(edge, spring) {
|
t.layout.eachEdge(function(edge, spring) {
|
||||||
|
@ -576,3 +568,31 @@ Renderer.prototype.start = function() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Array.forEach implementation for IE support..
|
||||||
|
//https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach
|
||||||
|
if ( !Array.prototype.forEach ) {
|
||||||
|
Array.prototype.forEach = function( callback, thisArg ) {
|
||||||
|
var T, k;
|
||||||
|
if ( this == null ) {
|
||||||
|
throw new TypeError( " this is null or not defined" );
|
||||||
|
}
|
||||||
|
var O = Object(this);
|
||||||
|
var len = O.length >>> 0; // Hack to convert O.length to a UInt32
|
||||||
|
if ( {}.toString.call(callback) != "[object Function]" ) {
|
||||||
|
throw new TypeError( callback + " is not a function" );
|
||||||
|
}
|
||||||
|
if ( thisArg ) {
|
||||||
|
T = thisArg;
|
||||||
|
}
|
||||||
|
k = 0;
|
||||||
|
while( k < len ) {
|
||||||
|
var kValue;
|
||||||
|
if ( k in O ) {
|
||||||
|
kValue = O[ k ];
|
||||||
|
callback.call( T, kValue, k, O );
|
||||||
|
}
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
14
springyui.js
14
springyui.js
|
@ -112,7 +112,7 @@ jQuery.fn.springy = function(params) {
|
||||||
});
|
});
|
||||||
|
|
||||||
Node.prototype.getWidth = function() {
|
Node.prototype.getWidth = function() {
|
||||||
var text = typeof(this.data.label) !== 'undefined' ? this.data.label : this.id;
|
var text = (this.data.label !== undefined) ? this.data.label : this.id;
|
||||||
if (this._width && this._width[text])
|
if (this._width && this._width[text])
|
||||||
return this._width[text];
|
return this._width[text];
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ jQuery.fn.springy = function(params) {
|
||||||
return 20;
|
return 20;
|
||||||
};
|
};
|
||||||
|
|
||||||
var renderer = new Renderer(1, layout,
|
var renderer = new Renderer(layout,
|
||||||
function clear() {
|
function clear() {
|
||||||
ctx.clearRect(0,0,canvas.width,canvas.height);
|
ctx.clearRect(0,0,canvas.width,canvas.height);
|
||||||
},
|
},
|
||||||
|
@ -174,18 +174,18 @@ jQuery.fn.springy = function(params) {
|
||||||
intersection = s2;
|
intersection = s2;
|
||||||
}
|
}
|
||||||
|
|
||||||
var stroke = typeof(edge.data.color) !== 'undefined' ? edge.data.color : '#000000';
|
var stroke = (edge.data.color !== undefined) ? edge.data.color : '#000000';
|
||||||
|
|
||||||
var arrowWidth;
|
var arrowWidth;
|
||||||
var arrowLength;
|
var arrowLength;
|
||||||
|
|
||||||
var weight = typeof(edge.data.weight) !== 'undefined' ? edge.data.weight : 1.0;
|
var weight = (edge.data.weight !== undefined) ? edge.data.weight : 1.0;
|
||||||
|
|
||||||
ctx.lineWidth = Math.max(weight * 2, 0.1);
|
ctx.lineWidth = Math.max(weight * 2, 0.1);
|
||||||
arrowWidth = 1 + ctx.lineWidth;
|
arrowWidth = 1 + ctx.lineWidth;
|
||||||
arrowLength = 8;
|
arrowLength = 8;
|
||||||
|
|
||||||
var directional = typeof(edge.data.directional) !== 'undefined' ? edge.data.directional : true;
|
var directional = (edge.data.directional !== undefined) ? edge.data.directional : true;
|
||||||
|
|
||||||
// line
|
// line
|
||||||
var lineEnd;
|
var lineEnd;
|
||||||
|
@ -218,7 +218,7 @@ jQuery.fn.springy = function(params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// label
|
// label
|
||||||
if (typeof(edge.data.label) !== 'undefined') {
|
if (edge.data.label !== undefined) {
|
||||||
text = edge.data.label
|
text = edge.data.label
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.textAlign = "center";
|
ctx.textAlign = "center";
|
||||||
|
@ -256,7 +256,7 @@ jQuery.fn.springy = function(params) {
|
||||||
ctx.font = "16px Verdana, sans-serif";
|
ctx.font = "16px Verdana, sans-serif";
|
||||||
ctx.fillStyle = "#000000";
|
ctx.fillStyle = "#000000";
|
||||||
ctx.font = "16px Verdana, sans-serif";
|
ctx.font = "16px Verdana, sans-serif";
|
||||||
var text = typeof(node.data.label) !== 'undefined' ? node.data.label : node.id;
|
var text = (node.data.label !== undefined) ? node.data.label : node.id;
|
||||||
ctx.fillText(text, s.x - boxWidth/2 + 5, s.y - 8);
|
ctx.fillText(text, s.x - boxWidth/2 + 5, s.y - 8);
|
||||||
|
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user