Author: marekz
Date: 2011-07-27 06:00:28 -0700 (Wed, 27 Jul 2011)
New Revision: 26280

Added:
   cytoscapeweb/branches/gsoc2011/cytoscapeweb-svg/springy.js
Modified:
   cytoscapeweb/branches/gsoc2011/cytoscapeweb-svg/cytoscapeweb-svg.js
   cytoscapeweb/branches/gsoc2011/cytoscapeweb-svg/main.html
Log:
Refactored arrowheads and added arrow color styles. Introduced springy.js to 
test FDL stability and FF/IE9 compatibility. Cleaned up demo code.

Modified: cytoscapeweb/branches/gsoc2011/cytoscapeweb-svg/cytoscapeweb-svg.js
===================================================================
--- cytoscapeweb/branches/gsoc2011/cytoscapeweb-svg/cytoscapeweb-svg.js 
2011-07-26 23:40:38 UTC (rev 26279)
+++ cytoscapeweb/branches/gsoc2011/cytoscapeweb-svg/cytoscapeweb-svg.js 
2011-07-27 13:00:28 UTC (rev 26280)
@@ -5,7 +5,11 @@
  * Google Summer of Code 2011
  */
 
-
+/* TODO
+ * - Move SVG DOM functions into a separate class
+ * - Move create*Element methods into the objects that use them
+ * - Switch some hand-built paths to generic ones with SVG transforms applied
+ */
 /**
  * Network visualization instance.
  * Represents an instance of the network visualization.
@@ -30,7 +34,9 @@
        
        this._setElementAttributes = function(elem, attributeMap) {
                for (var attribute in attributeMap) {
-                       elem.setAttribute(attribute, attributeMap[attribute]);
+                       var value = attributeMap[attribute];
+                       if (value != null)
+                       elem.setAttribute(attribute, value);
                }
        };
        
@@ -45,29 +51,33 @@
        this._svgNodeGroup = this._createSvgElement("g", this._svgElem);        
        this._svgLabelGroup = this._createSvgElement("g", this._svgElem);
        
-       this._createNodeGroup = function() {
-               
-       }
-       
        this._createLabelElement = function () {
                return this._createSvgElement("text", this._svgLabelGroup);
        };
        
        this._createEdgePathElement = function() {
                return this._createSvgElement("path", this._svgEdgeGroup);
-       }
+       };
        
        this._createNodePathElement = function() {
                return this._createSvgElement("path", this._svgNodeGroup);
-       }
+       };
        
        this._removeNodePathElement = function(elem) {
                this._svgNodeGroup.removeChild(elem);
-       }
+       };
        
        this._removeEdgePathElement = function(elem) {
                this._svgEdgeGroup.removeChild(elem);
-       }
+       };
+       
+       this._createLabelElement = function() {
+               return this._createSvgElement("text", this._svgLabelGroup);
+       };
+       
+       this._removeLabelElement = function(elem) {
+               this._svgLabelGroup.removeChild(elem);
+       };
        // --------
 
        this._edges = {};
@@ -80,6 +90,13 @@
        this._offsetX = 186;
        this._offsetY = 100;
 
+
+       this._styleDefaults = {
+               global: {},
+               node: {},
+               edge: {}
+       };
+
        // Style defaults
        this._style = {
                global: {
@@ -114,8 +131,10 @@
                        "style": "SOLID",
                        "forwardArrowShape": "",
                        "forwardArrowSize": 10,
+                       "forwardArrowColor": "red",
                        "backwardArrowShape": "",
-                       "backwardArrowSize": 10
+                       "backwardArrowSize": 10,
+                       "backwardArrowColor": "black"
                }
        };
        
@@ -616,7 +635,7 @@
 
        this._visualization = vis;
        this._elem = null;
-       this._elemLabel = null;
+       this._labelElem = null;
        this._group = "node";
        this._listeners = {};
        this._eventTypes = ["dragStart", "dragEnd", "hoverStart", "hoverEnd", 
"click", "select", "deselect"];
@@ -901,6 +920,7 @@
        this.remove = function() {
                if (this._elem) {
                        this._visualization._removeNodePathElement(this._elem);
+                       if (this._labelElem) 
this._visualization._removeLabelElement(this._labelElem);
                        
                }
                //if (this._elemLabel) this._elemLabel.remove();
@@ -919,8 +939,8 @@
        this._visualization = vis;
        this._elem = null;
        this._elemLabel = null;
-       this._elemArrowForward = null;
-       this._elemArrowBackward = null;
+       this._forwardArrowElem = null;
+       this._backwardArrowElem = null;
        this._group = "edge";
        this._offset = 0; // Offset used for drawing multiple edges between two 
nodes
        this._label = "";
@@ -940,6 +960,12 @@
                if (this._elem) {
                        this._visualization._removeEdgePathElement(this._elem);
                }
+               if (this._forwardArrowElem) {
+                       
this._visualization._removeEdgePathElement(this._forwardArrowElem);
+               }
+               if (this._backwardArrowElem) {
+                       
this._visualization._removeEdgePathElement(this._backwardArrowElem);
+               }
                this._source = null;
                this._target = null;
        };
@@ -960,6 +986,8 @@
                if (this._elem == null) {
                                this._elem = 
this._visualization._createEdgePathElement();
                                //this._elem = 
this._visualization._canvas.path(this._getSvgPath()).toBack().click(Util.delegate(this,
 "_onClick")).attr("cursor", "pointer");
+                               this._forwardArrowElem = 
this._visualization._createEdgePathElement();
+                               this._backwardArrowElem = 
this._visualization._createEdgePathElement();
                }
                
                var dashArray = {
@@ -975,16 +1003,14 @@
                        // Update position
                        "d": paths[0],
                        "stroke-dasharray" : dashArray,
-                       "fill": "none"
+                       "fill": "none",
+                       "stroke-linecap": "square"
                };
                // TODO: render paths[1] and paths[2], which are the arrows.
                
 
                var attrMap = {
                        "stroke": "color",
-                       //"fill": "color", // this is not applicable for curved 
paths
-                       
-               
                        "stroke-width": "width",
                        "stroke-opacity": "opacity"
                };
@@ -997,7 +1023,35 @@
                }
 
                this._visualization._setElementAttributes(this._elem, attr);
-               //this._elem.attr(attr);
+               
+               // Arrow head attributes
+               // 1. Forward arrow
+               var forwardArrowColor = 
this.getRenderedStyle("forwardArrowColor") || attr["stroke"];
+               var forwardArrowAttr = {
+                       "fill":  forwardArrowColor,
+                       "stroke": forwardArrowColor,
+                       "stroke-width": attr["stroke-width"],
+                       "stroke-opacity": attr["stroke-opacity"], // TODO: 
change to rely only on fill, rather than on stroke
+                       // because opacity
+                       "opacity": attr["stroke-opacity"],
+                       "d": paths[1] || null
+               };
+               
+               
this._visualization._setElementAttributes(this._forwardArrowElem, 
forwardArrowAttr);
+               
+               // 2. Backward arrow
+               var backwardArrowColor = 
this.getRenderedStyle("backwardArrowColor") || attr["stroke"];
+               var backwardArrowAttr = {
+                       "fill": backwardArrowColor,
+                       "stroke": backwardArrowColor,
+                       "stroke-width": attr["stroke-width"],
+                       "stroke-opacity": attr["stroke-opacity"],
+                       "opacity": attr["stroke-opacity"],
+                       "d": paths[2] || null
+               };
+               
this._visualization._setElementAttributes(this._backwardArrowElem, 
backwardArrowAttr);
+
+               
        };
        
        this._onClick = function() {
@@ -1104,11 +1158,11 @@
                                var bpy = b.y + bcy;
                                
 
-                               var path = [];
-                               path.push(populateString("M %,% Q %,% %,%", 
[apx, apy, cx, cy, bpx, bpy]));
+                               var path = new Array(3);
+                               path[0] = (populateString("M %,% Q %,% %,%", 
[apx, apy, cx, cy, bpx, bpy]));
                                
-                               if (this.getRenderedStyle("forwardArrowShape") 
== "DELTA") path.push(Shapes.buildArrow(apx, apy, 6, 10, Math.atan2(-acy, 
-acx), 1));
-                               if (this.getRenderedStyle("backwardArrowShape") 
== "DELTA") path.push(Shapes.buildArrow(bpx, bpy, 6, 10, Math.atan2(-bcy, 
-bcx), 1));
+                               if (this.getRenderedStyle("forwardArrowShape") 
== "DELTA") path[1] = (Shapes.buildArrow(apx, apy, 6, 10, Math.atan2(-acy, 
-acx), 1));
+                               if (this.getRenderedStyle("backwardArrowShape") 
== "DELTA") path[2] = (Shapes.buildArrow(bpx, bpy, 6, 10, Math.atan2(-bcy, 
-bcx), 1));
                                
                                return path;
                        
@@ -1200,3 +1254,76 @@
        });
 }
 
+
+/**
+ * Arbor.js Renderer
+ */
+ 
+function ArborRenderer(vis) {
+       this.vis = vis;
+       
+       this.init = function(ps) {
+               this.ps = ps;
+               ps.screenSize(320, 320);
+               ps.screenPadding(10);
+       }
+       
+       this.redraw = function() {
+               this.ps.eachNode(function(node, pt) {
+                       vis.getNode(node.name).setPosition(pt.x, pt.y);
+               });
+       }
+       
+       this.die = function() {
+               this.vis = null;
+               this.ps = null;
+       }
+}
+
+/**
+ * Springy.js renderer
+ */
+
+var SpringyRenderer = function() {
+       var vis;
+       var graph;
+       var layout;
+       
+               this.stop = function() {
+                       if (layout.intervalId) {
+                               layout.stop();
+                       }
+               }
+               
+               this.render = function(v) {
+                       vis = v;
+                       graph = new Graph();
+                       var nodes = vis.getNodes();
+                       for (var i = 0; i < nodes.length; i++) {
+                               graph.newNodeM(nodes[i]._id); 
+                       }
+                       var edges = vis.getEdges();
+                       for (var i = 0; i < edges.length; i++) {
+                               graph.newEdgeM(edges[i]._source._id,
+                                edges[i]._target._id); 
+                       }
+                       if (layout) layout.stop();
+                       layout = new Layout.ForceDirected(graph, 200, 200, 0.3);
+                       
+                       
+                       
+                       renderer = new Renderer(10, layout,
+                       function clear() {
+
+                       },
+                       function drawEdge(edge, p1, p2) {
+                               
+                       },
+                       function drawNode(node, p) {
+                               //console.log(p);
+                               //alert(JSON.stringify(p));
+                               vis.getNode(node.data.label).setPosition(p.x * 
100 + 200,  p.y * 100 +200);
+                       });
+                       renderer.start();
+               }
+};

Modified: cytoscapeweb/branches/gsoc2011/cytoscapeweb-svg/main.html
===================================================================
--- cytoscapeweb/branches/gsoc2011/cytoscapeweb-svg/main.html   2011-07-26 
23:40:38 UTC (rev 26279)
+++ cytoscapeweb/branches/gsoc2011/cytoscapeweb-svg/main.html   2011-07-27 
13:00:28 UTC (rev 26280)
@@ -10,12 +10,14 @@
                                margin: 30px auto;
                        }
                </style>
+               <script type="text/javascript" src="springy.js"></script>
                <script type="text/javascript" 
src="cytoscapeweb-svg.js"></script>
                <script type="text/javascript" src="jquery-1.6.2.js"></script>
-               <script type="text/javascript" src="arbor.js"></script>
+               <!-- <script type="text/javascript" src="arbor.js"></script> -->
+               
                <script type="text/javascript">
                
-                       var graphs = {};
+
                        var styles = {};
                        
                        styles.basicStyle = {
@@ -28,7 +30,6 @@
                                        borderColor: "#666666",
                                        borderWidth: 3,
                                        opacity: 1,
-                                       
                                },
                                edges: {
                                        color: "#999999",
@@ -36,12 +37,13 @@
                                        opacity: 0.8
                                }
                        }
-               
+                       
+                       
                        styles.demoStyle = {
                                global: {
                                        backgroundColor: "#fcfcfc"
                                },
-                               nodes: {                
+                               nodes: {
                                        color: "#cccccc",
                                        size: 14,
                                        borderWidth: 2,
@@ -54,6 +56,7 @@
                                }
                        }
                        
+                       
                        styles.darkStyle = {
                                global: {
                                        backgroundColor: "#202020"
@@ -71,92 +74,377 @@
                                }
                        }
                        
+                       
+                       var graphs = {};
+                       
                        graphs.simpleGraph = {
-                               nodes: [
-                                       {id: "n1", x: 220, y: 30},
-                                       {id: "n2", x: 0, y: 97},
-                                       {id: "n3", x: 54, y: 185},
-                                       {id: "n4", x: 200, y: 191},
-                                       {id: "n5", x: 210, y: 254},
-                                       {id: "n6", x: 60, y: -5},
-                                       {id: "n7", x: 280, y: 170}
-                               ],
-                               edges: [
-                                       {id: "e1", source: "n1", target: "n2", 
style: {forwardArrowShape: "DELTA"}},
-                                       {id: "e2", source: "n2", target: "n1", 
style: {forwardArrowShape: "DELTA"}},
-                                       {id: "e3", source: "n1", target: "n3", 
style: {forwardArrowShape: "DELTA"}},
-                                       {id: "e4", source: "n2", target: "n3", 
style: {forwardArrowShape: "DELTA"}},
-                                       {id: "e5", source: "n4", target: "n5", 
style: {forwardArrowShape: "DELTA"}},
-                                       {id: "e6", source: "n5", target: "n2", 
style: {forwardArrowShape: "DELTA"}},
-                                       {id: "e7", source: "n3", target: "n5", 
style: {forwardArrowShape: "DELTA"}},
-                                       {id: "e8", source: "n1", target: "n4", 
style: {forwardArrowShape: "DELTA"}},
-                                       {id: "e9", source: "n3", target: "n4", 
style: {forwardArrowShape: "DELTA"}},
-                                       {id: "e10", source: "n4", target: "n6", 
style: {forwardArrowShape: "DELTA"}},
-                                       {id: "e11", source: "n6", target: "n4", 
 style: {forwardArrowShape: "DELTA"}},
-                                       {id: "e12", source: "n3", target: "n6", 
style: {forwardArrowShape: "DELTA"}},
-                                       {id: "e14", source: "n4", target: "n7", 
style: {forwardArrowShape: "DELTA", backwardArrowShape: "DELTA"}},
-                                       {id: "e13", source: "n4", target: "n7", 
style: {forwardArrowShape: "DELTA"}},
-                                       {id: "e15", source: "n7", target: "n4", 
style: {forwardArrowShape: "DELTA"}},
-                                       
-                                       
-                               ]
+                               nodes: [{
+                                       id: "n1",
+                                       x: 220,
+                                       y: 30
+                               }, {
+                                       id: "n2",
+                                       x: 0,
+                                       y: 97
+                               }, {
+                                       id: "n3",
+                                       x: 54,
+                                       y: 185
+                               }, {
+                                       id: "n4",
+                                       x: 200,
+                                       y: 191
+                               }, {
+                                       id: "n5",
+                                       x: 210,
+                                       y: 254
+                               }, {
+                                       id: "n6",
+                                       x: 60,
+                                       y: -5
+                               }, {
+                                       id: "n7",
+                                       x: 280,
+                                       y: 170
+                               }],
+                               edges: [{
+                                       id: "e1",
+                                       source: "n1",
+                                       target: "n2",
+                                       style: {
+                                               forwardArrowShape: "DELTA"
+                                       }
+                               }, {
+                                       id: "e2",
+                                       source: "n2",
+                                       target: "n1",
+                                       style: {
+                                               forwardArrowShape: "DELTA"
+                                       }
+                               }, {
+                                       id: "e3",
+                                       source: "n1",
+                                       target: "n3",
+                                       style: {
+                                               forwardArrowShape: "DELTA"
+                                       }
+                               }, {
+                                       id: "e4",
+                                       source: "n2",
+                                       target: "n3",
+                                       style: {
+                                               forwardArrowShape: "DELTA"
+                                       }
+                               }, {
+                                       id: "e5",
+                                       source: "n4",
+                                       target: "n5",
+                                       style: {
+                                               forwardArrowShape: "DELTA"
+                                       }
+                               }, {
+                                       id: "e6",
+                                       source: "n5",
+                                       target: "n2",
+                                       style: {
+                                               forwardArrowShape: "DELTA"
+                                       }
+                               }, {
+                                       id: "e7",
+                                       source: "n3",
+                                       target: "n5",
+                                       style: {
+                                               forwardArrowShape: "DELTA"
+                                       }
+                               }, {
+                                       id: "e8",
+                                       source: "n1",
+                                       target: "n4",
+                                       style: {
+                                               forwardArrowShape: "DELTA"
+                                       }
+                               }, {
+                                       id: "e9",
+                                       source: "n3",
+                                       target: "n4",
+                                       style: {
+                                               forwardArrowShape: "DELTA"
+                                       }
+                               }, {
+                                       id: "e10",
+                                       source: "n4",
+                                       target: "n6",
+                                       style: {
+                                               forwardArrowShape: "DELTA"
+                                       }
+                               }, {
+                                       id: "e11",
+                                       source: "n6",
+                                       target: "n4",
+                                       style: {
+                                               forwardArrowShape: "DELTA"
+                                       }
+                               }, {
+                                       id: "e12",
+                                       source: "n3",
+                                       target: "n6",
+                                       style: {
+                                               forwardArrowShape: "DELTA"
+                                       }
+                               }, {
+                                       id: "e14",
+                                       source: "n4",
+                                       target: "n7",
+                                       style: {
+                                               forwardArrowShape: "DELTA",
+                                               backwardArrowShape: "DELTA"
+                                       }
+                               }, {
+                                       id: "e13",
+                                       source: "n4",
+                                       target: "n7",
+                                       style: {
+                                               forwardArrowShape: "DELTA"
+                                       }
+                               }, {
+                                       id: "e15",
+                                       source: "n7",
+                                       target: "n4",
+                                       style: {
+                                               forwardArrowShape: "DELTA"
+                                       }
+                               }, ]
                        }
-               
                        graphs.petersenGraph = {
-                               nodes: [
-                                       {id: "nw2", x: -27, y: 97},
-                                       {id: "sw2", x: 24, y: 255},
-                                       {id: "se2", x: 210, y: 254},
-                                       {id: "ne2", x: 254, y: 90},
-                                       {id: "n2", x: 109, y: -10},
-                                       {id: "sw", x: 73, y: 190},
-                                       {id: "se", x: 154, y: 190},
-                                       {id: "nw", x: 46, y: 115},
-                                       {id: "ne", x: 176, y: 113},
-                                       {id: "n", x: 109, y: 65}
-                               ],
-                               edges: [
-                                       {id: "e1", source: "nw2", target: "n2"},
-                                       {id: "e2", source: "nw2", target: "nw"},
-                                       {id: "e4", source: "sw2", target: 
"nw2"},
-                                       {id: "e5", source: "sw2", target: "sw"},
-                                       {id: "e6", source: "se2", target: 
"sw2"},
-                                       {id: "e7", source: "se2", target: "se"},
-                                       {id: "e8", source: "ne2", target: 
"se2"},
-                                       {id: "e9", source: "ne2", target: "ne"},
-                                       {id: "e10", source: "n2", target: 
"ne2"},
-                                       {id: "e11", source: "n2", target: "n"},
-                                       {id: "e12", source: "sw", target: "se"},
-                                       {id: "e13", source: "nw", target: "sw"},
-                                       {id: "e14", source: "nw", target: "se"},
-                                       {id: "e15", source: "nw", target: "ne"},
-                                       {id: "e16", source: "ne", target: "sw"},
-                                       {id: "e17", source: "ne", target: "se"},
-                                       {id: "e18", source: "n", target: "sw"},
-                                       {id: "e19", source: "n", target: "se"},
-                                       {id: "e20", source: "n", target: "nw"},
-                                       {id: "e21", source: "n", target: "ne"},
-                               ]       
+                               nodes: [{
+                                       id: "nw2",
+                                       x: -27,
+                                       y: 97
+                               }, {
+                                       id: "sw2",
+                                       x: 24,
+                                       y: 255
+                               }, {
+                                       id: "se2",
+                                       x: 210,
+                                       y: 254
+                               }, {
+                                       id: "ne2",
+                                       x: 254,
+                                       y: 90
+                               }, {
+                                       id: "n2",
+                                       x: 109,
+                                       y: -10
+                               }, {
+                                       id: "sw",
+                                       x: 73,
+                                       y: 190
+                               }, {
+                                       id: "se",
+                                       x: 154,
+                                       y: 190
+                               }, {
+                                       id: "nw",
+                                       x: 46,
+                                       y: 115
+                               }, {
+                                       id: "ne",
+                                       x: 176,
+                                       y: 113
+                               }, {
+                                       id: "n",
+                                       x: 109,
+                                       y: 65
+                               }],
+                               edges: [{
+                                       id: "e1",
+                                       source: "nw2",
+                                       target: "n2"
+                               }, {
+                                       id: "e2",
+                                       source: "nw2",
+                                       target: "nw"
+                               }, {
+                                       id: "e4",
+                                       source: "sw2",
+                                       target: "nw2"
+                               }, {
+                                       id: "e5",
+                                       source: "sw2",
+                                       target: "sw"
+                               }, {
+                                       id: "e6",
+                                       source: "se2",
+                                       target: "sw2"
+                               }, {
+                                       id: "e7",
+                                       source: "se2",
+                                       target: "se"
+                               }, {
+                                       id: "e8",
+                                       source: "ne2",
+                                       target: "se2"
+                               }, {
+                                       id: "e9",
+                                       source: "ne2",
+                                       target: "ne"
+                               }, {
+                                       id: "e10",
+                                       source: "n2",
+                                       target: "ne2"
+                               }, {
+                                       id: "e11",
+                                       source: "n2",
+                                       target: "n"
+                               }, {
+                                       id: "e12",
+                                       source: "sw",
+                                       target: "se"
+                               }, {
+                                       id: "e13",
+                                       source: "nw",
+                                       target: "sw"
+                               }, {
+                                       id: "e14",
+                                       source: "nw",
+                                       target: "se"
+                               }, {
+                                       id: "e15",
+                                       source: "nw",
+                                       target: "ne"
+                               }, {
+                                       id: "e16",
+                                       source: "ne",
+                                       target: "sw"
+                               }, {
+                                       id: "e17",
+                                       source: "ne",
+                                       target: "se"
+                               }, {
+                                       id: "e18",
+                                       source: "n",
+                                       target: "sw"
+                               }, {
+                                       id: "e19",
+                                       source: "n",
+                                       target: "se"
+                               }, {
+                                       id: "e20",
+                                       source: "n",
+                                       target: "nw"
+                               }, {
+                                       id: "e21",
+                                       source: "n",
+                                       target: "ne"
+                               }, ]
                        }
                        
+                       
                        graphs.ringGraph = {
-                               "nodes": [
-                                       
{"id":"n1","x":38,"y":219,"style":{size: 32, borderColor: "black", color: 
"teal", shape: "DIAMOND", borderWidth: 4}},
-                                       {"id":"n2","x":57,"y":44,"style":{size: 
26, color: "chocolate", borderWidth: 6}},
-                                       
{"id":"n3","x":171,"y":28,"style":{size: 16, borderColor: "green", shape: 
"RECTANGLE", borderWidth: 2}},
-                                       
{"id":"n4","x":251,"y":132,"style":{size: 12, borderColor: "blue", shape: 
"DIAMOND", borderWidth:4}},
-                                       
{"id":"n5","x":156,"y":220,"style":{borderColor: "red"}}
-                               ],
-                               "edges": [
-                                       
{"id":"e1","source":"n1","target":"n2","style":{forwardArrowShape: "DELTA", 
color: "#394", width: 3}},
-                                       
{"id":"e2","source":"n2","target":"n3","style":{forwardArrowShape: "DELTA", 
backwardArrowShape: "DELTA", width: 3}},
-                                       
{"id":"e3","source":"n3","target":"n4","style":{color: "red", width: 6, style: 
"DASH"}},
-                                       
{"id":"e4","source":"n4","target":"n5","style":{forwardArrowShape: "DELTA", 
color: "blue", width: 5}},
-                                       
{"id":"e5","source":"n5","target":"n4","style":{forwardArrowShape: "DELTA", 
width: 5}},
-                                       
{"id":"e6","source":"n5","target":"n1","style":{style: "DOT"}}
-                               ]
+                               "nodes": [{
+                                       "id": "n1",
+                                       "x": 38,
+                                       "y": 219,
+                                       "style": {
+                                               size: 32,
+                                               borderColor: "black",
+                                               color: "teal",
+                                               shape: "DIAMOND",
+                                               borderWidth: 4
+                                       }
+                               }, {
+                                       "id": "n2",
+                                       "x": 57,
+                                       "y": 44,
+                                       "style": {
+                                               size: 26,
+                                               color: "chocolate",
+                                               borderWidth: 6
+                                       }
+                               }, {
+                                       "id": "n3",
+                                       "x": 171,
+                                       "y": 28,
+                                       "style": {
+                                               size: 16,
+                                               borderColor: "green",
+                                               shape: "RECTANGLE",
+                                               borderWidth: 2
+                                       }
+                               }, {
+                                       "id": "n4",
+                                       "x": 251,
+                                       "y": 132,
+                                       "style": {
+                                               size: 12,
+                                               borderColor: "blue",
+                                               shape: "DIAMOND",
+                                               borderWidth: 4
+                                       }
+                               }, {
+                                       "id": "n5",
+                                       "x": 156,
+                                       "y": 220,
+                                       "style": {
+                                               borderColor: "red"
+                                       }
+                               }],
+                               "edges": [{
+                                       "id": "e1",
+                                       "source": "n1",
+                                       "target": "n2",
+                                       "style": {
+                                               forwardArrowShape: "DELTA",
+                                               color: "#394",
+                                               width: 3
+                                       }
+                               }, {
+                                       "id": "e2",
+                                       "source": "n2",
+                                       "target": "n3",
+                                       "style": {
+                                               forwardArrowShape: "DELTA",
+                                               backwardArrowShape: "DELTA",
+                                               width: 3
+                                       }
+                               }, {
+                                       "id": "e3",
+                                       "source": "n3",
+                                       "target": "n4",
+                                       "style": {
+                                               color: "red",
+                                               width: 6,
+                                               style: "DASH"
+                                       }
+                               }, {
+                                       "id": "e4",
+                                       "source": "n4",
+                                       "target": "n5",
+                                       "style": {
+                                               forwardArrowShape: "DELTA",
+                                               color: "blue",
+                                               width: 5
+                                       }
+                               }, {
+                                       "id": "e5",
+                                       "source": "n5",
+                                       "target": "n4",
+                                       "style": {
+                                               forwardArrowShape: "DELTA",
+                                               width: 5
+                                       }
+                               }, {
+                                       "id": "e6",
+                                       "source": "n5",
+                                       "target": "n1",
+                                       "style": {
+                                               style: "DOT"
+                                       }
+                               }]
                        }
                        
+                       // Create a populated graph for stress testing
                        var pg = {nodes:[], edges:[]};
                        
                        for (var x = 0; x < 100; x++) {
@@ -168,24 +456,29 @@
                        }
                        graphs.pg = pg;
                        
+                       // Initialize
                        var vis;
                        window.onload = function() {
                                vis = new Visualization("container-div", 640, 
640);
+                               sr = new SpringyRenderer();
                                load("simpleGraph");
                                setstyle("basicStyle");
                        }
-                       var ps;
+                       
+                       // Load a graph
+                       var ps; // arbor.ParticleSystem
+                       var sr; // springyrenderer
                        function load(graph) {
                                vis.loadGraph(graphs[graph]);
-                               
+                               /*
                                if (ps) {
-                                       ps.renderer.delete();
+                                       ps.renderer.die();
                                        ps.stop();
                                        ps = null;
                                }
                                ps = arbor.ParticleSystem(800, 600, 0.5);
                                ps.parameters({gravity: true});
-                               ps.renderer = new Renderer(vis);
+                               ps.renderer = new ArborRenderer(vis);
                                
                                var nodes = vis.getNodes();
                                for (var i = 0; i < nodes.length; i++) {
@@ -199,8 +492,8 @@
                                        ps.addEdge(edges[i]._source._id,
                                         edges[i]._target._id); 
                                }
-                               
-                               
+                               */
+                               sr.render(vis);
                                /*
                                var clickEvent = function() {
                                        alert("Clicked node '" + this._id + "' 
at " + this._x + ", " + this._y);
@@ -218,28 +511,9 @@
                                vis.draw();
                        }
 
-                       function Renderer(vis) {
-                               this.vis = vis;
-                               
-                               this.init = function(ps) {
-                                       this.ps = ps;
-                                       ps.screenSize(320, 320);
-                                       ps.screenPadding(10);
-                               }
-                               
-                               this.redraw = function() {
-                                       this.ps.eachNode(function(node, pt) {
-                                               
vis.getNode(node.name).setPosition(pt.x, pt.y);
-                                       });
-                               }
-                               
-                               this.delete = function() {
-                                       this.vis = null;
-                                       this.ps = null;
-                               }
-                       }
 
 
+
                        
                        
                                

Added: cytoscapeweb/branches/gsoc2011/cytoscapeweb-svg/springy.js
===================================================================
--- cytoscapeweb/branches/gsoc2011/cytoscapeweb-svg/springy.js                  
        (rev 0)
+++ cytoscapeweb/branches/gsoc2011/cytoscapeweb-svg/springy.js  2011-07-27 
13:00:28 UTC (rev 26280)
@@ -0,0 +1,621 @@
+/**
+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.
+*/
+
+var Graph = function()
+{
+       this.nodeCache = {}; // Added mz
+       this.nodeSet = {};
+       this.nodes = [];
+       this.edges = [];
+       this.adjacency = {};
+
+       this.nextNodeId = 0;
+       this.nextEdgeId = 0;
+       this.eventListeners = [];
+};
+
+
+
+SpringyNode = function(id, data)
+{
+       this.id = id;
+       this.data = typeof(data) !== 'undefined' ? data : {};
+};
+
+SpringyEdge = function(id, source, target, data)
+{
+       this.id = id;
+       this.source = source;
+       this.target = target;
+       this.data = typeof(data) !== 'undefined' ? data : {};
+};
+
+Graph.prototype.addNode = function(node)
+{
+       if (typeof(this.nodeSet[node.id]) === 'undefined')
+       {
+               this.nodes.push(node);
+       }
+
+       this.nodeSet[node.id] = node;
+
+       this.notify();
+       return node;
+};
+
+Graph.prototype.addEdge = function(edge)
+{
+       var exists = false;
+       this.edges.forEach(function(e){
+               if (edge.id === e.id) { exists = true; }
+       });
+
+       if (!exists)
+       {
+               this.edges.push(edge);
+       }
+
+       if (typeof(this.adjacency[edge.source.id]) === 'undefined')
+       {
+               this.adjacency[edge.source.id] = {};
+       }
+       if (typeof(this.adjacency[edge.source.id][edge.target.id]) === 
'undefined')
+       {
+               this.adjacency[edge.source.id][edge.target.id] = [];
+       }
+
+       exists = false;
+       this.adjacency[edge.source.id][edge.target.id].forEach(function(e){
+                       if (edge.id === e.id) { exists = true; }
+       });
+
+       if (!exists)
+       {
+               this.adjacency[edge.source.id][edge.target.id].push(edge);
+       }
+
+       this.notify();
+       return edge;
+};
+
+Graph.prototype.newNode = function(data)
+{
+       var node = new SpringyNode(this.nextNodeId++, data);
+       this.addNode(node);
+       return node;
+};
+
+Graph.prototype.newEdge = function(source, target, data)
+{
+       var edge = new SpringyEdge(this.nextEdgeId++, source, target, data);
+       this.addEdge(edge);
+       return edge;
+};
+
+
+// find the edges from node1 to node2
+Graph.prototype.getEdges = function(node1, node2)
+{
+       if (typeof(this.adjacency[node1.id]) !== 'undefined'
+               && typeof(this.adjacency[node1.id][node2.id]) !== 'undefined')
+       {
+               return this.adjacency[node1.id][node2.id];
+       }
+
+       return [];
+};
+
+// remove a node and it's associated edges from the graph
+Graph.prototype.removeNode = function(node)
+{
+       if (typeof(this.nodeSet[node.id]) !== 'undefined')
+       {
+               delete this.nodeSet[node.id];
+       }
+
+       for (var i = this.nodes.length - 1; i >= 0; i--)
+       {
+               if (this.nodes[i].id === node.id)
+               {
+                       this.nodes.splice(i, 1);
+               }
+       }
+
+       var tmpEdges = this.edges.slice();
+       tmpEdges.forEach(function(e) {
+               if (e.source.id === node.id || e.target.id === node.id)
+               {
+                       this.removeEdge(e);
+               }
+       }, this);
+
+       this.notify();
+};
+
+
+// remove a node and it's associated edges from the graph
+Graph.prototype.removeEdge = function(edge)
+{
+       for (var i = this.edges.length - 1; i >= 0; i--)
+       {
+               if (this.edges[i].id === edge.id)
+               {
+                       this.edges.splice(i, 1);
+               }
+       }
+
+       for (var x in this.adjacency)
+       {
+               for (var y in this.adjacency[x])
+               {
+                       var edges = this.adjacency[x][y];
+
+                       for (var j=edges.length - 1; j>=0; j--)
+                       {
+                               if (this.adjacency[x][y][j].id === edge.id)
+                               {
+                                       this.adjacency[x][y].splice(j, 1);
+                               }
+                       }
+               }
+       }
+
+       this.notify();
+};
+
+/* Merge a list of nodes and edges into the current graph. eg.
+var o = {
+       nodes: [
+               {id: 123, data: {type: 'user', userid: 123, displayname: 
'aaa'}},
+               {id: 234, data: {type: 'user', userid: 234, displayname: 'bbb'}}
+       ],
+       edges: [
+               {from: 0, to: 1, type: 'submitted_design', directed: true, 
data: {weight: }}
+       ]
+}
+*/
+Graph.prototype.merge = function(data)
+{
+       var nodes = [];
+       data.nodes.forEach(function(n) {
+               nodes.push(this.addNode(new SpringyNode(n.id, n.data)));
+       }, this);
+
+       data.edges.forEach(function(e) {
+               var from = nodes[e.from];
+               var to = nodes[e.to];
+
+               var id = (e.directed)
+                       ? (id = e.type + "-" + from.id + "-" + to.id)
+                       : (from.id < to.id) // normalise id for non-directed 
edges
+                               ? e.type + "-" + from.id + "-" + to.id
+                               : e.type + "-" + to.id + "-" + from.id;
+
+               var edge = this.addEdge(new SpringyEdge(id, from, to, e.data));
+               edge.data.type = e.type;
+       }, this);
+};
+
+Graph.prototype.filterNodes = function(fn)
+{
+       var tmpNodes = this.nodes.slice();
+       tmpNodes.forEach(function(n) {
+               if (!fn(n))
+               {
+                       this.removeNode(n);
+               }
+       }, this);
+};
+
+Graph.prototype.filterEdges = function(fn)
+{
+       var tmpEdges = this.edges.slice();
+       tmpEdges.forEach(function(e) {
+               if (!fn(e))
+               {
+                       this.removeEdge(e);
+               }
+       }, this);
+};
+
+
+Graph.prototype.addGraphListener = function(obj)
+{
+       this.eventListeners.push(obj);
+};
+
+Graph.prototype.notify = function()
+{
+       this.eventListeners.forEach(function(obj){
+               obj.graphChanged();
+       });
+};
+
+// -----------
+var Layout = {};
+Layout.ForceDirected = function(graph, stiffness, repulsion, damping)
+{
+       this.graph = graph;
+       this.stiffness = stiffness; // spring stiffness constant
+       this.repulsion = repulsion; // repulsion constant
+       this.damping = damping; // velocity damping factor
+
+       this.nodePoints = {}; // keep track of points associated with nodes
+       this.edgeSprings = {}; // keep track of springs associated with edges
+
+       this.intervalId = null;
+};
+
+Layout.ForceDirected.prototype.point = function(node)
+{
+       if (typeof(this.nodePoints[node.id]) === 'undefined')
+       {
+               var mass = typeof(node.data.mass) !== 'undefined' ? 
node.data.mass : 1.0;
+               this.nodePoints[node.id] = new 
Layout.ForceDirected.Point(Vector.random(), mass);
+       }
+
+       return this.nodePoints[node.id];
+};
+
+Layout.ForceDirected.prototype.spring = function(edge)
+{
+       if (typeof(this.edgeSprings[edge.id]) === 'undefined')
+       {
+               var length = typeof(edge.data.length) !== 'undefined' ? 
edge.data.length : 1.0;
+
+               var existingSpring = false;
+
+               var from = this.graph.getEdges(edge.source, edge.target);
+               from.forEach(function(e){
+                       if (existingSpring === false && 
typeof(this.edgeSprings[e.id]) !== 'undefined') {
+                               existingSpring = this.edgeSprings[e.id];
+                       }
+               }, this);
+
+               if (existingSpring !== false) {
+                       return new 
Layout.ForceDirected.Spring(existingSpring.point1, existingSpring.point2, 0.0, 
0.0);
+               }
+
+               var to = this.graph.getEdges(edge.target, edge.source);
+               from.forEach(function(e){
+                       if (existingSpring === false && 
typeof(this.edgeSprings[e.id]) !== 'undefined') {
+                               existingSpring = this.edgeSprings[e.id];
+                       }
+               }, this);
+
+               if (existingSpring !== false) {
+                       return new 
Layout.ForceDirected.Spring(existingSpring.point2, existingSpring.point1, 0.0, 
0.0);
+               }
+
+               this.edgeSprings[edge.id] = new Layout.ForceDirected.Spring(
+                       this.point(edge.source), this.point(edge.target), 
length, this.stiffness
+               );
+       }
+
+       return this.edgeSprings[edge.id];
+};
+
+// callback should accept two arguments: SpringyNode, Point
+Layout.ForceDirected.prototype.eachNode = function(callback)
+{
+       var t = this;
+       this.graph.nodes.forEach(function(n){
+               callback.call(t, n, t.point(n));
+       });
+};
+
+// callback should accept two arguments: SpringyEdge, Spring
+Layout.ForceDirected.prototype.eachEdge = function(callback)
+{
+       var t = this;
+       this.graph.edges.forEach(function(e){
+               callback.call(t, e, t.spring(e));
+       });
+};
+
+// callback should accept one argument: Spring
+Layout.ForceDirected.prototype.eachSpring = function(callback)
+{
+       var t = this;
+       this.graph.edges.forEach(function(e){
+               callback.call(t, t.spring(e));
+       });
+};
+
+
+// Physics stuff
+Layout.ForceDirected.prototype.applyCoulombsLaw = function()
+{
+       this.eachNode(function(n1, point1) {
+               this.eachNode(function(n2, point2) {
+                       if (point1 !== point2)
+                       {
+                               var d = point1.p.subtract(point2.p);
+                               var distance = d.magnitude() + 1.0;
+                               var direction = d.normalise();
+
+                               // apply force to each end point
+                               
point1.applyForce(direction.multiply(this.repulsion).divide(distance * distance 
* 0.5));
+                               
point2.applyForce(direction.multiply(this.repulsion).divide(distance * distance 
* -0.5));
+                       }
+               });
+       });
+};
+
+Layout.ForceDirected.prototype.applyHookesLaw = function()
+{
+       this.eachSpring(function(spring){
+               var d = spring.point2.p.subtract(spring.point1.p); // the 
direction of the spring
+               var displacement = spring.length - d.magnitude();
+               var direction = d.normalise();
+
+               // apply force to each end point
+               spring.point1.applyForce(direction.multiply(spring.k * 
displacement * -0.5));
+               spring.point2.applyForce(direction.multiply(spring.k * 
displacement * 0.5));
+       });
+};
+
+Layout.ForceDirected.prototype.attractToCentre = function()
+{
+       this.eachNode(function(node, point) {
+               var direction = point.p.multiply(-1.0);
+               point.applyForce(direction.multiply(this.repulsion / 50.0));
+       });
+};
+
+
+Layout.ForceDirected.prototype.updateVelocity = function(timestep)
+{
+       this.eachNode(function(node, point) {
+               point.v = 
point.v.add(point.f.multiply(timestep)).multiply(this.damping);
+               point.f = new Vector(0,0);
+       });
+};
+
+Layout.ForceDirected.prototype.updatePosition = function(timestep)
+{
+       this.eachNode(function(node, point) {
+               point.p = point.p.add(point.v.multiply(timestep));
+       });
+};
+
+Layout.ForceDirected.prototype.totalEnergy = function(timestep)
+{
+       var energy = 0.0;
+       this.eachNode(function(node, point) {
+               var speed = point.v.magnitude();
+               energy += speed * speed;
+       });
+
+       return energy;
+};
+
+
+// start simulation
+Layout.ForceDirected.prototype.start = function(interval, render, done)
+{
+       var t = this;
+
+       if (this.intervalId !== null) {
+               return; // already running
+       }
+
+       this.intervalId = setInterval(function() {
+               t.applyCoulombsLaw();
+               t.applyHookesLaw();
+               t.attractToCentre();
+               t.updateVelocity(0.03);
+               t.updatePosition(0.03);
+
+               if (typeof(render) !== 'undefined') { render(); }
+
+               // stop simulation when energy of the system goes below a 
threshold
+               if (t.totalEnergy() < 0.1)
+               {
+                       t.stop();
+                       if (typeof(done) !== 'undefined') { done(); }
+               }
+       }, interval);
+};
+
+// added mz
+Layout.ForceDirected.prototype.stop = function() {
+       clearInterval(this.intervalId);
+       this.intervalId = null;
+};
+
+// Find the nearest point to a particular position
+Layout.ForceDirected.prototype.nearest = function(pos)
+{
+       var min = {node: null, point: null, distance: null};
+       var t = this;
+       this.graph.nodes.forEach(function(n){
+               var point = t.point(n);
+               var distance = point.p.subtract(pos).magnitude();
+
+               if (min.distance === null || distance < min.distance)
+               {
+                       min = {node: n, point: point, distance: distance};
+               }
+       });
+
+       return min;
+};
+
+// returns [bottomleft, topright]
+Layout.ForceDirected.prototype.getBoundingBox = function()
+{
+       var bottomleft = new Vector(-2,-2);
+       var topright = new Vector(2,2);
+
+       this.eachNode(function(n, point) {
+               if (point.p.x < bottomleft.x) {
+                       bottomleft.x = point.p.x;
+               }
+               if (point.p.y < bottomleft.y) {
+                       bottomleft.y = point.p.y;
+               }
+               if (point.p.x > topright.x) {
+                       topright.x = point.p.x;
+               }
+               if (point.p.y > topright.y) {
+                       topright.y = point.p.y;
+               }
+       });
+
+       var padding = topright.subtract(bottomleft).multiply(0.07); // 5% 
padding
+
+       return {bottomleft: bottomleft.subtract(padding), topright: 
topright.add(padding)};
+};
+
+
+// Vector
+Vector = function(x, y)
+{
+       this.x = x;
+       this.y = y;
+};
+
+Vector.random = function()
+{
+       return new Vector(10.0 * (Math.random() - 0.5), 10.0 * (Math.random() - 
0.5));
+};
+
+Vector.prototype.add = function(v2)
+{
+       return new Vector(this.x + v2.x, this.y + v2.y);
+};
+
+Vector.prototype.subtract = function(v2)
+{
+       return new Vector(this.x - v2.x, this.y - v2.y);
+};
+
+Vector.prototype.multiply = function(n)
+{
+       return new Vector(this.x * n, this.y * n);
+};
+
+Vector.prototype.divide = function(n)
+{
+       return new Vector(this.x / n, this.y / n);
+};
+
+Vector.prototype.magnitude = function()
+{
+       return Math.sqrt(this.x*this.x + this.y*this.y);
+};
+
+Vector.prototype.normal = function()
+{
+       return new Vector(-this.y, this.x);
+};
+
+Vector.prototype.normalise = function()
+{
+       return this.divide(this.magnitude());
+};
+
+// Point
+Layout.ForceDirected.Point = function(position, mass)
+{
+       this.p = position; // position
+       this.m = mass; // mass
+       this.v = new Vector(0, 0); // velocity
+       this.f = new Vector(0, 0); // force
+};
+
+Layout.ForceDirected.Point.prototype.applyForce = function(force)
+{
+       this.f = this.f.add(force.divide(this.m));
+};
+
+// Spring
+Layout.ForceDirected.Spring = function(point1, point2, length, k)
+{
+       this.point1 = point1;
+       this.point2 = point2;
+       this.length = length; // spring length at rest
+       this.k = k; // spring constant (See Hooke's law) .. how stiff the 
spring is
+};
+
+// Layout.ForceDirected.Spring.prototype.distanceToPoint = function(point)
+// {
+//     // hardcore vector arithmetic.. ohh yeah!
+//     // .. see 
http://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment/865080#865080
+//     var n = this.point2.p.subtract(this.point1.p).normalise().normal();
+//     var ac = point.p.subtract(this.point1.p);
+//     return Math.abs(ac.x * n.x + ac.y * n.y);
+// };
+
+// Renderer handles the layout rendering loop
+function Renderer(interval, layout, clear, drawEdge, drawNode)
+{
+       this.interval = interval;
+       this.layout = layout;
+       this.clear = clear;
+       this.drawEdge = drawEdge;
+       this.drawNode = drawNode;
+
+       this.layout.graph.addGraphListener(this);
+}
+
+Renderer.prototype.graphChanged = function(e)
+{
+       this.start();
+};
+
+Renderer.prototype.start = function()
+{
+       var t = this;
+       this.layout.start(50, function render() {
+               t.clear();
+
+               t.layout.eachEdge(function(edge, spring) {
+                       t.drawEdge(edge, spring.point1.p, spring.point2.p);
+               });
+
+               t.layout.eachNode(function(node, point) {
+                       t.drawNode(node, point.p);
+               });
+       });
+};
+
+
+/**
+ * Extension by MZ
+ * Allow adding nodes and edges by string id only
+ */
+Graph.prototype.newNodeM = function(id, data) {
+       data = data || {};
+       data.label = id;
+       this.nodeCache[id] = this.newNode(data);
+
+}
+
+Graph.prototype.newEdgeM = function(sourceId, targetId, data)
+{
+       this.newEdge(this.nodeCache[sourceId], this.nodeCache[targetId], data);
+};

-- 
You received this message because you are subscribed to the Google Groups 
"cytoscape-cvs" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/cytoscape-cvs?hl=en.

Reply via email to