📚 The CoCalc Library - books, templates and other resources
cocalc-examples / martinthoma-latex-examples / presentations / Diskrete-Mathematik / isomorph-graph-example / graph1-js / graph.js
132939 viewsLicense: OTHER
/* Graph JavaScript framework, version 0.0.11* (c) 2006 Aslak Hellesoy <[email protected]>2* (c) 2006 Dave Hoover <[email protected]>3*4* Ported from Graph::Layouter::Spring in5* http://search.cpan.org/~pasky/Graph-Layderer-0.02/6* The algorithm is based on a spring-style layouter of a Java-based social7* network tracker PieSpy written by Paul Mutton E<lt>[email protected]<gt>.8*9* Adopted by Philipp Strathausen <[email protected]> to support Raphael JS10* for rendering, dragging and much more. See http://blog.ameisenbar.de11*12* Graph is freely distributable under the terms of an MIT-style license.13* For details, see the Graph web site: http://dev.buildpatternd.com/trac14*15* Links:16*17* Demo of the original applet:18* http://redsquirrel.com/dave/work/webdep/19*20* Mirrored original source code at snipplr:21* http://snipplr.com/view/1950/graph-javascript-framework-version-001/22*23* Original usage example:24* http://ajaxian.com/archives/new-javascriptcanvas-graph-library25*26/*--------------------------------------------------------------------------*/2728/*29* Graph30*/31var Graph = function() {32this.nodes = [];33this.edges = [];34};35Graph.prototype = {36addNode: function(id, content) {37/* testing if node is already existing in the graph */38var new_node = this.nodes[id];39if(new_node == undefined) {40new_node = new Graph.Node(id, content||{"id":id});41this.nodes[id] = new_node;42this.nodes.push(new_node); // TODO get rid of the array43}44return new_node;45},4647addEdge: function(source, target, style) {48var s = this.addNode(source);49var t = this.addNode(target);50var color;51var colorbg;52var directed;53if(style) { color = style.color; colorbg = style.colorbg; directed = style.directed }54var edge = { source: s, target: t, color: color, colorbg: colorbg, directed: directed };55this.edges.push(edge);56}57};5859/*60* Node61*/62Graph.Node = function(id, value){63this.id = id;64this.content = value;65};66Graph.Node.prototype = {67};68Graph.Renderer = {};69Graph.Renderer.Raphael = function(element, graph, width, height) {70this.width = width||400;71this.height = height||400;72var selfRef = this;73this.r = Raphael(element, this.width, this.height);74this.radius = 40; /* max dimension of a node */75this.graph = graph;76this.mouse_in = false;7778/*79* Dragging80*/81this.isDrag = false;82this.dragger = function (e) {83this.dx = e.clientX;84this.dy = e.clientY;85selfRef.isDrag = this;86this.animate({"fill-opacity": .2}, 500);87e.preventDefault && e.preventDefault();88};8990document.onmousemove = function (e) {91e = e || window.event;92if (selfRef.isDrag) {93var newX = e.clientX - selfRef.isDrag.dx + (selfRef.isDrag.attrs.cx == null ? (selfRef.isDrag.attrs.x + selfRef.isDrag.attrs.width / 2) : selfRef.isDrag.attrs.cx);94var newY = e.clientY - selfRef.isDrag.dy + (selfRef.isDrag.attrs.cy == null ? (selfRef.isDrag.attrs.y + selfRef.isDrag.attrs.height / 2) : selfRef.isDrag.attrs.cy);95/* prevent shapes from being dragged out of the canvas */96var clientX = e.clientX - (newX < 20 ? newX - 20 : newX > selfRef.width - 20 ? newX - selfRef.width + 20 : 0);97var clientY = e.clientY - (newY < 20 ? newY - 20 : newY > selfRef.height - 20 ? newY - selfRef.height + 20 : 0);98selfRef.isDrag.translate(clientX - selfRef.isDrag.dx, clientY - selfRef.isDrag.dy);99selfRef.isDrag.label.translate(clientX - selfRef.isDrag.dx, clientY - selfRef.isDrag.dy);100for (var i in selfRef.graph.edges) {101selfRef.graph.edges[i].connection.draw();102}103//selfRef.r.safari();104selfRef.isDrag.dx = clientX;105selfRef.isDrag.dy = clientY;106}107};108document.onmouseup = function () {109selfRef.isDrag && selfRef.isDrag.animate({"fill-opacity": 0}, 500);110selfRef.isDrag = false;111};112};113114/*115* Renderer using RaphaelJS116*/117Graph.Renderer.Raphael.prototype = {118translate: function(point) {119return [120(point[0] - this.graph.layoutMinX) * this.factorX + this.radius,121(point[1] - this.graph.layoutMinY) * this.factorY + this.radius122];123},124125rotate: function(point, length, angle) {126var dx = length * Math.cos(angle);127var dy = length * Math.sin(angle);128return [point[0]+dx, point[1]+dy];129},130131draw: function() {132this.factorX = (width - 2 * this.radius) / (this.graph.layoutMaxX - this.graph.layoutMinX);133this.factorY = (height - 2 * this.radius) / (this.graph.layoutMaxY - this.graph.layoutMinY);134for (var i = 0; i < this.graph.nodes.length; i++) {135this.drawNode(this.graph.nodes[i]);136}137for (var i = 0; i < this.graph.edges.length; i++) {138this.drawEdge(this.graph.edges[i]);139}140},141drawNode: function(node) {142var point = this.translate([node.layoutPosX, node.layoutPosY]);143node.point = point;144145/* if node has already been drawn, move the nodes */146if(node.shape) {147// console.log(node.shape.attrs );148var opoint = [ node.shape.attrs.cx || node.shape.attrs.x + node.shape.attrs.width / 2 , node.shape.attrs.cy || node.shape.attrs.y + node.shape.attrs.height / 2 + 15 ];149node.shape.translate(point[0]-opoint[0], point[1]-opoint[1]);150node.shape.label.translate(point[0]-opoint[0], point[1]-opoint[1]);151this.r.safari();152return;153}154var shape;155if(node.content.getShape) {156shape = node.content.getShape(this.r, point[0], point[1]);157shape.attr({"fill-opacity": 0});158} else {159shape = this.r.ellipse(point[0], point[1], 30, 20);160var color = Raphael.getColor();161shape.attr({fill: color, stroke: color, "fill-opacity": 0, "stroke-width": 2})162}163shape.mousedown(this.dragger);164shape.node.style.cursor = "move";165shape.label = this.r.text(point[0], point[1] + 30, node.content.label || node.id); // Beware: operator || also considers values like -1, 0, ...166node.shape = shape;167},168drawEdge: function(edge) {169/* if edge already has been drawn, only refresh the edge */170edge.connection && edge.connection.draw();171if(!edge.connection)172edge.connection = this.r.connection(edge.source.shape, edge.target.shape, { fg: edge.color, bg: edge.colorbg, directed: edge.directed });173}174};175Graph.Layout = {};176Graph.Layout.Spring = function(graph) {177this.graph = graph;178this.iterations = 500;179this.maxRepulsiveForceDistance = 6;180this.k = 2;181this.c = 0.01;182this.maxVertexMovement = 0.5;183};184Graph.Layout.Spring.prototype = {185layout: function() {186this.layoutPrepare();187for (var i = 0; i < this.iterations; i++) {188this.layoutIteration();189}190this.layoutCalcBounds();191},192193layoutPrepare: function() {194for (var i = 0; i < this.graph.nodes.length; i++) {195var node = this.graph.nodes[i];196node.layoutPosX = 0;197node.layoutPosY = 0;198node.layoutForceX = 0;199node.layoutForceY = 0;200}201202},203204layoutCalcBounds: function() {205var minx = Infinity, maxx = -Infinity, miny = Infinity, maxy = -Infinity;206207for (var i = 0; i < this.graph.nodes.length; i++) {208var x = this.graph.nodes[i].layoutPosX;209var y = this.graph.nodes[i].layoutPosY;210211if(x > maxx) maxx = x;212if(x < minx) minx = x;213if(y > maxy) maxy = y;214if(y < miny) miny = y;215}216217this.graph.layoutMinX = minx;218this.graph.layoutMaxX = maxx;219this.graph.layoutMinY = miny;220this.graph.layoutMaxY = maxy;221},222223layoutIteration: function() {224// Forces on nodes due to node-node repulsions225for (var i = 0; i < this.graph.nodes.length; i++) {226var node1 = this.graph.nodes[i];227for (var j = i + 1; j < this.graph.nodes.length; j++) {228var node2 = this.graph.nodes[j];229this.layoutRepulsive(node1, node2);230}231}232// Forces on nodes due to edge attractions233for (var i = 0; i < this.graph.edges.length; i++) {234var edge = this.graph.edges[i];235this.layoutAttractive(edge);236}237238// Move by the given force239for (var i = 0; i < this.graph.nodes.length; i++) {240var node = this.graph.nodes[i];241var xmove = this.c * node.layoutForceX;242var ymove = this.c * node.layoutForceY;243244var max = this.maxVertexMovement;245if(xmove > max) xmove = max;246if(xmove < -max) xmove = -max;247if(ymove > max) ymove = max;248if(ymove < -max) ymove = -max;249250node.layoutPosX += xmove;251node.layoutPosY += ymove;252node.layoutForceX = 0;253node.layoutForceY = 0;254}255},256257layoutRepulsive: function(node1, node2) {258var dx = node2.layoutPosX - node1.layoutPosX;259var dy = node2.layoutPosY - node1.layoutPosY;260var d2 = dx * dx + dy * dy;261if(d2 < 0.01) {262dx = 0.1 * Math.random() + 0.1;263dy = 0.1 * Math.random() + 0.1;264var d2 = dx * dx + dy * dy;265}266var d = Math.sqrt(d2);267if(d < this.maxRepulsiveForceDistance) {268var repulsiveForce = this.k * this.k / d;269node2.layoutForceX += repulsiveForce * dx / d;270node2.layoutForceY += repulsiveForce * dy / d;271node1.layoutForceX -= repulsiveForce * dx / d;272node1.layoutForceY -= repulsiveForce * dy / d;273}274},275276layoutAttractive: function(edge) {277var node1 = edge.source;278var node2 = edge.target;279280var dx = node2.layoutPosX - node1.layoutPosX;281var dy = node2.layoutPosY - node1.layoutPosY;282var d2 = dx * dx + dy * dy;283if(d2 < 0.01) {284dx = 0.1 * Math.random() + 0.1;285dy = 0.1 * Math.random() + 0.1;286var d2 = dx * dx + dy * dy;287}288var d = Math.sqrt(d2);289if(d > this.maxRepulsiveForceDistance) {290d = this.maxRepulsiveForceDistance;291d2 = d * d;292}293var attractiveForce = (d2 - this.k * this.k) / this.k;294if(edge.weight == undefined || edge.weight < 1) edge.weight = 1;295attractiveForce *= Math.log(edge.weight) * 0.5 + 1;296297node2.layoutForceX -= attractiveForce * dx / d;298node2.layoutForceY -= attractiveForce * dy / d;299node1.layoutForceX += attractiveForce * dx / d;300node1.layoutForceY += attractiveForce * dy / d;301}302};303304305