فهرست منبع

fixed minor issues

Martin Thoma 12 سال پیش
والد
کامیت
753becae8b

+ 13 - 13
presentations/Diskrete-Mathematik/Handout/Handout.tex

@@ -125,10 +125,13 @@ $K \subseteq E \times E$ die
 Kantenmenge bezeichnet.
 \end{definition}
 
-\begin{definition}{Inzidenz}
-Sei $e \in E$ und $k = \Set{e_1, e_2} \in K$.
+\begin{definition}{Grad einer Ecke}
+Der \textbf{Grad} einer Ecke ist die Anzahl der Kanten, die von dieser Ecke
+ausgehen.
+\end{definition}
 
-$e$ heißt \textbf{inzident} zu $k :\Leftrightarrow e = e_1$ oder $e = e_2$
+\begin{definition}{Isolierte Ecke}
+Hat eine Ecke den Grad 0, so nennt man ihn \textbf{isoliert}.
 \end{definition}
 
 \begin{definition}{Schlinge}
@@ -137,6 +140,12 @@ Sei $G=(E, K)$ ein Graph und $k=\Set{e_1, e_2} \in K$ eine Kante.
 $k$ heißt \textbf{Schlinge} $:\Leftrightarrow e_1 = e_2$ 
 \end{definition}
 
+\begin{definition}{Inzidenz}
+Sei $e \in E$ und $k = \Set{e_1, e_2} \in K$.
+
+$e$ heißt \textbf{inzident} zu $k :\Leftrightarrow e = e_1$ oder $e = e_2$
+\end{definition}
+
 \begin{definition}{Vollständiger Graph}
 Sei $G = (E, K)$ ein Graph.
 
@@ -189,7 +198,7 @@ Sei $G = (E, K)$ ein Graph und $A = (k_1, k_2 \dots, k_s)$ ein Kantenzug.
 A heißt \textbf{Weg} $:\Leftrightarrow \forall_{i, j \in 1, \dots, s}: i \neq j \Rightarrow k_i \neq k_j$ .
 \end{definition}
 
-\begin{definition}{Einfacher Kreis}
+\begin{definition}{Kreis}
 Sei $G = (E, K)$ ein Graph und $A = (k_1, k_2 \dots, k_s)$ ein Kantenzug.
 
 A heißt \textbf{Kreis} $:\Leftrightarrow A$ ist geschlossen und ein Weg.
@@ -216,15 +225,6 @@ $G$ heißt \textbf{zusammenhängend} $:\Leftrightarrow \forall e_1, e_2 \in E: $
 Es ex. ein Kantenzug, der $e_1$ und $e_2$ verbindet
 \end{definition}
 
-\begin{definition}{Grad einer Ecke}
-Der \textbf{Grad} einer Ecke ist die Anzahl der Kanten, die von dieser Ecke
-ausgehen.
-\end{definition}
-
-\begin{definition}{Isolierte Ecke}
-Hat eine Ecke den Grad 0, so nennt man ihn \textbf{isoliert}.
-\end{definition}
-
 \begin{definition}{Eulerscher Kreis}
 Sei $G$ ein Graph und $A$ ein Kreis in $G$.
 

BIN
presentations/Diskrete-Mathematik/LaTeX/Graphentheorie-I.pdf


+ 1 - 1
presentations/Diskrete-Mathematik/LaTeX/Koenigsberger-Brueckenproblem.tex

@@ -34,7 +34,7 @@ Verwechslungsgefahr: Hamiltonkreis $\neq$ Eulerkreis
 \begin{block}{Hamiltonkreis}
 Sei $G$ ein Graph und $A$ ein Kreis in $G$.
 
-$A$ heißt \textbf{Hamilton-Kreis} $:\Leftrightarrow \forall_{e \in E}: e \in A$.
+$A$ heißt \textbf{Hamilton-Kreis} $:\Leftrightarrow \forall_{e \in E}: e \text{ ist genau ein mal in } A$.
 \end{block}
 
 \begin{block}{Eulerscher Kreis}

+ 0 - 4
presentations/Diskrete-Mathematik/LaTeX/Strukturen.tex

@@ -63,10 +63,6 @@ Ein Kantenzug wird durch den Tupel $(e_0, \dots, e_s) \in E^{s+1}$
 charakterisiert.
 
 \begin{gallery}
-    \galleryimage{walks/walk-1}
-    \galleryimage{walks/walk-2}
-    \galleryimage{walks/k-3-3-walk}
-    \galleryimage{walks/k-5-walk}\\
     \galleryimage{walks/k-16-walk}
     \galleryimage{walks/star-graph-walk}
     \galleryimage{walks/tree-walk}

+ 6 - 0
presentations/Diskrete-Mathematik/isomorph-graph-example/README.md

@@ -0,0 +1,6 @@
+See [StackExchange](http://stackoverflow.com/q/7034/562769) for many
+visualization options. The best I've seen are:
+
+* [Graph Dracula](http://www.graphdracula.net/)
+* [jsPlumb](http://jsplumbtoolkit.com/)
+* [graph-it](http://js-graph-it.sourceforge.net/)

+ 101 - 0
presentations/Diskrete-Mathematik/isomorph-graph-example/graph1-js/graffle.js

@@ -0,0 +1,101 @@
+/**
+ * Originally grabbed from the official RaphaelJS Documentation
+ * http://raphaeljs.com/graffle.html
+ * Changes and comments by Philipp Strathausen http://blog.ameisenbar.de
+ * Licenced under the MIT licence.
+ */
+
+/**
+ * Usage:
+ * connect two shapes
+ * parameters: 
+ *      source shape [or connection for redrawing], 
+ *      target shape,
+ *      style with { fg : linecolor, bg : background color, directed: boolean }
+ * returns:
+ *      connection { draw = function() }
+ */
+Raphael.fn.connection = function (obj1, obj2, style) {
+    var selfRef = this;
+    /* create and return new connection */
+    var color = style.fg || "#000";
+    var edge = {/*
+        from : obj1,
+        to : obj2,
+        style : style,*/
+        draw : function() {
+            /* get bounding boxes of target and source */
+            var bb1 = obj1.getBBox();
+            var bb2 = obj2.getBBox();
+            bb1.height = bb1.height + 15; /* respect the label TODO do it in a cleaner way */
+            bb2.height = bb2.height + 15;
+            var off1 = 1;
+            var off2 = 2;
+            /* coordinates for potential connection coordinates from/to the objects */
+            var p = [
+                {x: bb1.x + bb1.width / 2, y: bb1.y - off1},              /* NORTH 1 */
+                {x: bb1.x + bb1.width / 2, y: bb1.y + bb1.height + off1}, /* SOUTH 1 */
+                {x: bb1.x - off1, y: bb1.y + bb1.height / 2},             /* WEST  1 */
+                {x: bb1.x + bb1.width + off1, y: bb1.y + bb1.height / 2}, /* EAST  1 */
+                {x: bb2.x + bb2.width / 2, y: bb2.y - off2},              /* NORTH 2 */
+                {x: bb2.x + bb2.width / 2, y: bb2.y + bb2.height + off2}, /* SOUTH 2 */
+                {x: bb2.x - off2, y: bb2.y + bb2.height / 2},             /* WEST  2 */
+                {x: bb2.x + bb2.width + off2, y: bb2.y + bb2.height / 2}  /* EAST  2 */
+            ];
+            
+            /* distances between objects and according coordinates connection */
+            var d = {}, dis = [];
+
+            /*
+             * find out the best connection coordinates by trying all possible ways
+             */
+            /* loop the first object's connection coordinates */
+            for (var i = 0; i < 4; i++) {
+                /* loop the seond object's connection coordinates */
+                for (var j = 4; j < 8; j++) {
+                    var dx = Math.abs(p[i].x - p[j].x),
+                        dy = Math.abs(p[i].y - p[j].y);
+                    if ((i == j - 4) || (((i != 3 && j != 6) || p[i].x < p[j].x) && ((i != 2 && j != 7) || p[i].x > p[j].x) && ((i != 0 && j != 5) || p[i].y > p[j].y) && ((i != 1 && j != 4) || p[i].y < p[j].y))) {
+                        dis.push(dx + dy);
+                        d[dis[dis.length - 1]] = [i, j];
+                    }
+                }
+            }
+            var res = dis.length == 0 ? res = [0, 4] : d[Math.min.apply(Math, dis)];
+            /* bezier path */
+            var x1 = p[res[0]].x,
+                y1 = p[res[0]].y,
+                x4 = p[res[1]].x,
+                y4 = p[res[1]].y,
+                dx = Math.max(Math.abs(x1 - x4) / 2, 10),
+                dy = Math.max(Math.abs(y1 - y4) / 2, 10),
+                x2 = [x1, x1, x1 - dx, x1 + dx][res[0]].toFixed(3),
+                y2 = [y1 - dy, y1 + dy, y1, y1][res[0]].toFixed(3),
+                x3 = [0, 0, 0, 0, x4, x4, x4 - dx, x4 + dx][res[1]].toFixed(3),
+                y3 = [0, 0, 0, 0, y1 + dy, y1 - dy, y4, y4][res[1]].toFixed(3);
+            /* assemble path and arrow */
+            var path = ["M", x1.toFixed(3), y1.toFixed(3), "C", x2, y2, x3, y3, x4.toFixed(3), y4.toFixed(3)].join(",");
+            /* arrow */
+            if(style && style.directed) {
+                /* magnitude, length of the last path vector */
+                var mag = Math.sqrt((y4 - y3) * (y4 - y3) + (x4 - x3) * (x4 - x3));
+                /* vector normalisation to specified length  */
+                var norm = function(x,l){return (-x*(l||5)/mag);};
+                /* calculate array coordinates (two lines orthogonal to the path vector) */
+                var arr = [
+                    {x:(norm(x4-x3)+norm(y4-y3)+x4).toFixed(3), y:(norm(y4-y3)+norm(x4-x3)+y4).toFixed(3)},
+                    {x:(norm(x4-x3)-norm(y4-y3)+x4).toFixed(3), y:(norm(y4-y3)-norm(x4-x3)+y4).toFixed(3)}
+                ];
+                path = path + ",M"+arr[0].x+","+arr[0].y+",L"+x4+","+y4+",L"+arr[1].x+","+arr[1].y; 
+            }
+            
+            // applying path
+            edge.fg && edge.fg.attr({path:path}) 
+                || (edge.fg = selfRef.path(path).attr({stroke: color, fill: "none"}).toBack());
+            edge.bg && edge.bg.attr({path:path})
+                || style && style.bg && (edge.bg = style.bg.split && selfRef.path(path).attr({stroke: style.bg.split("|")[0], fill: "none", "stroke-width": style.bg.split("|")[1] || 3}).toBack());
+        }
+    }
+    edge.draw();
+    return edge;
+};

+ 303 - 0
presentations/Diskrete-Mathematik/isomorph-graph-example/graph1-js/graph.js

@@ -0,0 +1,303 @@
+/*  Graph JavaScript framework, version 0.0.1
+ *  (c) 2006 Aslak Hellesoy <aslak.hellesoy@gmail.com>
+ *  (c) 2006 Dave Hoover <dave.hoover@gmail.com>
+ *
+ *  Ported from Graph::Layouter::Spring in
+ *    http://search.cpan.org/~pasky/Graph-Layderer-0.02/
+ *  The algorithm is based on a spring-style layouter of a Java-based social
+ *  network tracker PieSpy written by Paul Mutton E<lt>paul@jibble.orgE<gt>.
+ *
+ *  Adopted by Philipp Strathausen <strathausen@gmail.com> to support Raphael JS
+ *  for rendering, dragging and much more. See http://blog.ameisenbar.de
+ *
+ *  Graph is freely distributable under the terms of an MIT-style license.
+ *  For details, see the Graph web site: http://dev.buildpatternd.com/trac
+ *
+ *  Links:
+ *
+ *  Demo of the original applet:
+ *      http://redsquirrel.com/dave/work/webdep/
+ *
+ *  Mirrored original source code at snipplr:
+ *      http://snipplr.com/view/1950/graph-javascript-framework-version-001/
+ *
+ *  Original usage example:
+ *      http://ajaxian.com/archives/new-javascriptcanvas-graph-library
+ *
+/*--------------------------------------------------------------------------*/
+
+/*
+ * Graph
+ */
+var Graph = function() {
+    this.nodes = [];
+    this.edges = [];
+};
+Graph.prototype = {
+    addNode: function(id, content) {
+        /* testing if node is already existing in the graph */
+        var new_node = this.nodes[id];
+        if(new_node == undefined) {
+                new_node = new Graph.Node(id, content||{"id":id});
+                this.nodes[id] = new_node;
+                this.nodes.push(new_node); // TODO get rid of the array
+        }
+        return new_node;
+    },
+
+    addEdge: function(source, target, style) {
+        var s = this.addNode(source);
+        var t = this.addNode(target);
+        var color;
+        var colorbg;
+        var directed;
+        if(style) { color = style.color; colorbg = style.colorbg; directed = style.directed }
+        var edge = { source: s, target: t, color: color, colorbg: colorbg, directed: directed };
+        this.edges.push(edge);
+    }
+};
+
+/*
+ * Node
+ */
+Graph.Node = function(id, value){
+    this.id = id;
+    this.content = value;
+};
+Graph.Node.prototype = {
+};
+Graph.Renderer = {};
+Graph.Renderer.Raphael = function(element, graph, width, height) {
+    this.width = width||400;
+    this.height = height||400;
+    var selfRef = this;
+    this.r = Raphael(element, this.width, this.height);
+    this.radius = 40; /* max dimension of a node */
+    this.graph = graph;
+    this.mouse_in = false;
+    
+    /*
+     * Dragging
+     */
+    this.isDrag = false;
+    this.dragger = function (e) {
+        this.dx = e.clientX;
+        this.dy = e.clientY;
+        selfRef.isDrag = this;
+        this.animate({"fill-opacity": .2}, 500);
+        e.preventDefault && e.preventDefault();
+    };
+
+    document.onmousemove = function (e) {
+        e = e || window.event;
+        if (selfRef.isDrag) {
+            var newX = e.clientX - selfRef.isDrag.dx + (selfRef.isDrag.attrs.cx == null ? (selfRef.isDrag.attrs.x + selfRef.isDrag.attrs.width / 2) : selfRef.isDrag.attrs.cx);
+            var newY = e.clientY - selfRef.isDrag.dy + (selfRef.isDrag.attrs.cy == null ? (selfRef.isDrag.attrs.y + selfRef.isDrag.attrs.height / 2) : selfRef.isDrag.attrs.cy);
+            /* prevent shapes from being dragged out of the canvas */
+            var clientX = e.clientX - (newX < 20 ? newX - 20 : newX > selfRef.width - 20 ? newX - selfRef.width + 20 : 0);
+            var clientY = e.clientY - (newY < 20 ? newY - 20 : newY > selfRef.height - 20 ? newY - selfRef.height + 20 : 0);
+            selfRef.isDrag.translate(clientX - selfRef.isDrag.dx, clientY - selfRef.isDrag.dy);
+            selfRef.isDrag.label.translate(clientX - selfRef.isDrag.dx, clientY - selfRef.isDrag.dy);
+            for (var i in selfRef.graph.edges) {
+                selfRef.graph.edges[i].connection.draw();
+            }
+            //selfRef.r.safari();
+            selfRef.isDrag.dx = clientX;
+            selfRef.isDrag.dy = clientY;
+        }
+    };
+    document.onmouseup = function () {
+        selfRef.isDrag && selfRef.isDrag.animate({"fill-opacity": 0}, 500);
+        selfRef.isDrag = false;
+    };
+};
+
+/*
+ * Renderer using RaphaelJS
+ */
+Graph.Renderer.Raphael.prototype = {
+    translate: function(point) {
+        return [
+            (point[0] - this.graph.layoutMinX) * this.factorX + this.radius,
+            (point[1] - this.graph.layoutMinY) * this.factorY + this.radius
+        ];
+    },
+
+    rotate: function(point, length, angle) {
+        var dx = length * Math.cos(angle);
+        var dy = length * Math.sin(angle);
+        return [point[0]+dx, point[1]+dy];
+    },
+
+    draw: function() {
+        this.factorX = (width - 2 * this.radius) / (this.graph.layoutMaxX - this.graph.layoutMinX);
+        this.factorY = (height - 2 * this.radius) / (this.graph.layoutMaxY - this.graph.layoutMinY);
+        for (var i = 0; i < this.graph.nodes.length; i++) {
+            this.drawNode(this.graph.nodes[i]);
+        }
+        for (var i = 0; i < this.graph.edges.length; i++) {
+            this.drawEdge(this.graph.edges[i]);
+        }
+    },
+    drawNode: function(node) {
+        var point = this.translate([node.layoutPosX, node.layoutPosY]);
+        node.point = point;
+        
+        /* if node has already been drawn, move the nodes */
+        if(node.shape) {
+//            console.log(node.shape.attrs );
+            var 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 ];
+            node.shape.translate(point[0]-opoint[0], point[1]-opoint[1]);
+            node.shape.label.translate(point[0]-opoint[0], point[1]-opoint[1]);
+            this.r.safari();
+            return;
+        }
+        var shape;
+        if(node.content.getShape) {
+            shape = node.content.getShape(this.r, point[0], point[1]);
+            shape.attr({"fill-opacity": 0});
+        } else {
+            shape = this.r.ellipse(point[0], point[1], 30, 20);
+            var color = Raphael.getColor();
+            shape.attr({fill: color, stroke: color, "fill-opacity": 0, "stroke-width": 2})
+        }
+        shape.mousedown(this.dragger);
+        shape.node.style.cursor = "move";
+        shape.label = this.r.text(point[0], point[1] + 30, node.content.label || node.id); // Beware: operator || also considers values like -1, 0, ...
+        node.shape = shape;
+    },
+    drawEdge: function(edge) {
+        /* if edge already has been drawn, only refresh the edge */
+        edge.connection && edge.connection.draw();
+        if(!edge.connection)
+            edge.connection = this.r.connection(edge.source.shape, edge.target.shape, { fg: edge.color, bg: edge.colorbg, directed: edge.directed });
+    }
+};
+Graph.Layout = {};
+Graph.Layout.Spring = function(graph) {
+                this.graph = graph;
+                this.iterations = 500;
+                this.maxRepulsiveForceDistance = 6;
+                this.k = 2;
+                this.c = 0.01;
+                this.maxVertexMovement = 0.5;
+        };
+Graph.Layout.Spring.prototype = {
+        layout: function() {
+                this.layoutPrepare();
+            for (var i = 0; i < this.iterations; i++) {
+                        this.layoutIteration();
+                }
+                this.layoutCalcBounds();
+        },
+       
+        layoutPrepare: function() {
+            for (var i = 0; i < this.graph.nodes.length; i++) {
+                    var node = this.graph.nodes[i];
+                        node.layoutPosX = 0;
+                        node.layoutPosY = 0;
+                        node.layoutForceX = 0;
+                        node.layoutForceY = 0;
+                }
+                
+        },
+       
+        layoutCalcBounds: function() {
+                var minx = Infinity, maxx = -Infinity, miny = Infinity, maxy = -Infinity;
+
+            for (var i = 0; i < this.graph.nodes.length; i++) {
+                        var x = this.graph.nodes[i].layoutPosX;
+                        var y = this.graph.nodes[i].layoutPosY;
+                                               
+                        if(x > maxx) maxx = x;
+                        if(x < minx) minx = x;
+                        if(y > maxy) maxy = y;
+                        if(y < miny) miny = y;
+                }
+
+                this.graph.layoutMinX = minx;
+                this.graph.layoutMaxX = maxx;
+                this.graph.layoutMinY = miny;
+                this.graph.layoutMaxY = maxy;
+        },
+       
+        layoutIteration: function() {
+                // Forces on nodes due to node-node repulsions
+            for (var i = 0; i < this.graph.nodes.length; i++) {
+                    var node1 = this.graph.nodes[i];
+                    for (var j = i + 1; j < this.graph.nodes.length; j++) {
+                            var node2 = this.graph.nodes[j];
+                                this.layoutRepulsive(node1, node2);
+                        }
+                }
+                // Forces on nodes due to edge attractions
+            for (var i = 0; i < this.graph.edges.length; i++) {
+                    var edge = this.graph.edges[i];
+                        this.layoutAttractive(edge);             
+                }
+               
+                // Move by the given force
+            for (var i = 0; i < this.graph.nodes.length; i++) {
+                    var node = this.graph.nodes[i];
+                        var xmove = this.c * node.layoutForceX;
+                        var ymove = this.c * node.layoutForceY;
+
+                        var max = this.maxVertexMovement;
+                        if(xmove > max) xmove = max;
+                        if(xmove < -max) xmove = -max;
+                        if(ymove > max) ymove = max;
+                        if(ymove < -max) ymove = -max;
+                       
+                        node.layoutPosX += xmove;
+                        node.layoutPosY += ymove;
+                        node.layoutForceX = 0;
+                        node.layoutForceY = 0;
+                }
+        },
+
+        layoutRepulsive: function(node1, node2) {
+                var dx = node2.layoutPosX - node1.layoutPosX;
+                var dy = node2.layoutPosY - node1.layoutPosY;
+                var d2 = dx * dx + dy * dy;
+                if(d2 < 0.01) {
+                        dx = 0.1 * Math.random() + 0.1;
+                        dy = 0.1 * Math.random() + 0.1;
+                        var d2 = dx * dx + dy * dy;
+                }
+                var d = Math.sqrt(d2);
+                if(d < this.maxRepulsiveForceDistance) {
+                        var repulsiveForce = this.k * this.k / d;
+                        node2.layoutForceX += repulsiveForce * dx / d;
+                        node2.layoutForceY += repulsiveForce * dy / d;
+                        node1.layoutForceX -= repulsiveForce * dx / d;
+                        node1.layoutForceY -= repulsiveForce * dy / d;
+                }
+        },
+
+        layoutAttractive: function(edge) {
+                var node1 = edge.source;
+                var node2 = edge.target;
+               
+                var dx = node2.layoutPosX - node1.layoutPosX;
+                var dy = node2.layoutPosY - node1.layoutPosY;
+                var d2 = dx * dx + dy * dy;
+                if(d2 < 0.01) {
+                        dx = 0.1 * Math.random() + 0.1;
+                        dy = 0.1 * Math.random() + 0.1;
+                        var d2 = dx * dx + dy * dy;
+                }
+                var d = Math.sqrt(d2);
+                if(d > this.maxRepulsiveForceDistance) {
+                        d = this.maxRepulsiveForceDistance;
+                        d2 = d * d;
+                }
+                var attractiveForce = (d2 - this.k * this.k) / this.k;
+                if(edge.weight == undefined || edge.weight < 1) edge.weight = 1;
+                attractiveForce *= Math.log(edge.weight) * 0.5 + 1;
+               
+                node2.layoutForceX -= attractiveForce * dx / d;
+                node2.layoutForceY -= attractiveForce * dy / d;
+                node1.layoutForceX += attractiveForce * dx / d;
+                node1.layoutForceY += attractiveForce * dy / d;
+        }
+};

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 7 - 0
presentations/Diskrete-Mathematik/isomorph-graph-example/graph1-js/raphael-min.js


+ 40 - 0
presentations/Diskrete-Mathematik/isomorph-graph-example/graph1.html

@@ -0,0 +1,40 @@
+<html>
+<header>
+    <script type="text/javascript" src="graph1-js/raphael-min.js"></script>
+    <script type="text/javascript" src="graph1-js/graffle.js"></script>
+    <script type="text/javascript" src="graph1-js/graph.js"></script>
+    <script type="text/javascript">
+        var redraw;
+        var height = 600;
+        var width = 800;
+
+        /* only do all this when document has finished loading (needed for RaphaelJS */
+        window.onload = function() {
+            var g = new Graph();
+
+            g.addEdge("n1", "n2");
+            g.addEdge("n1", "n3");
+            g.addEdge("n1", "n4");
+            g.addEdge("n2", "n3");
+            g.addEdge("n4", "n3");
+
+            /* layout the graph using the Spring layout implementation */
+            var layouter = new Graph.Layout.Spring(g);
+            layouter.layout();
+
+            /* draw the graph using the RaphaelJS draw implementation */
+            var renderer = new Graph.Renderer.Raphael('canvas', g, width, height);
+            renderer.draw();
+
+            redraw = function() {
+            layouter.layout();
+            renderer.draw();
+            };
+        };
+    </script>
+</header>
+<body>
+<div id="canvas"></div>
+<button id="redraw" onclick="redraw();">redraw</button>
+</body>
+</html>

+ 53 - 0
presentations/Diskrete-Mathematik/isomorph-graph-example/graph2.html

@@ -0,0 +1,53 @@
+<!doctype html>
+<html>
+<head>
+    <title>Graph example</title>
+    <meta http-equiv="content-type" content="text/html;charset=utf-8" />    
+    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js"></script>
+    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/jquery-ui.min.js"></script>
+    <script type="text/javascript" src="http://jsplumb.org/js/1.3.1/jquery.jsPlumb-1.3.1-all-min.js"></script>
+    <style>
+.node {
+    position: absolute;
+    height: 50px;
+    width: 50px;
+    line-height: 50px;
+
+    -moz-border-radius: 30px;
+    border-radius: 30px;
+
+    background-color: black;
+    color: white;
+    text-align: center;
+    font-size: 2em;
+}
+    </style>
+</head>
+<body >
+<div class="node" id="b1" style="top:100px;left:300px">1</div>
+<div class="node" id="b2" style="top:200px;left:500px;">2</div>
+<div class="node" id="b3" style="top:400px;left:400px;">3</div>
+<div class="node" id="b4" style="top:400px;left:200px;">4</div>
+<div class="node" id="b5" style="top:200px;left:100px;">5</div>
+
+
+<script type="text/javascript">
+
+jsPlumb.bind("ready", function() {
+
+
+jsPlumb.draggable('b1');
+jsPlumb.draggable('b2');
+jsPlumb.draggable('b3');
+jsPlumb.draggable('b4');
+jsPlumb.draggable('b5');
+jsPlumb.connect({source:"b1", target:"b2"});
+jsPlumb.connect({source:"b2", target:"b3"});
+jsPlumb.connect({source:"b3", target:"b4"});
+jsPlumb.connect({source:"b1", target:"b3"});
+});
+
+</script>
+
+</body>
+</html>