Project

General

Profile

Download (7.17 KB) Statistics
| Branch: | Revision:
1
/*!
2
Copyright (c) The Cytoscape Consortium
3

    
4
Permission is hereby granted, free of charge, to any person obtaining a copy of
5
this software and associated documentation files (the “Software”), to deal in
6
the Software without restriction, including without limitation the rights to
7
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8
of the Software, and to permit persons to whom the Software is furnished to do
9
so, subject to the following conditions:
10

    
11
The above copyright notice and this permission notice shall be included in all
12
copies or substantial portions of the Software.
13

    
14
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
SOFTWARE.
21
*/
22

    
23
;(function(){ 'use strict';
24

    
25
  // registers the extension on a cytoscape lib ref
26
  var register = function( cytoscape, dagre ){
27
    if( !cytoscape || !dagre ){ return; } // can't register if cytoscape unspecified
28

    
29
    var isFunction = function(o){ return typeof o === 'function'; };
30

    
31
    // default layout options
32
    var defaults = {
33
      // dagre algo options, uses default value on undefined
34
      nodeSep: undefined, // the separation between adjacent nodes in the same rank
35
      edgeSep: undefined, // the separation between adjacent edges in the same rank
36
      rankSep: undefined, // the separation between adjacent nodes in the same rank
37
      rankDir: undefined, // 'TB' for top to bottom flow, 'LR' for left to right
38
      minLen: function( edge ){ return 1; }, // number of ranks to keep between the source and target of the edge
39
      edgeWeight: function( edge ){ return 1; }, // higher weight edges are generally made shorter and straighter than lower weight edges
40

    
41
      // general layout options
42
      fit: true, // whether to fit to viewport
43
      padding: 30, // fit padding
44
      animate: false, // whether to transition the node positions
45
      animationDuration: 500, // duration of animation in ms if enabled
46
      animationEasing: undefined, // easing of animation if enabled
47
      boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
48
      ready: function(){}, // on layoutready
49
      stop: function(){} // on layoutstop
50
    };
51

    
52
    // constructor
53
    // options : object containing layout options
54
    function DagreLayout( options ){
55
      var opts = this.options = {};
56
      for( var i in defaults ){ opts[i] = defaults[i]; }
57
      for( var i in options ){ opts[i] = options[i]; }
58
    }
59

    
60
    // runs the layout
61
    DagreLayout.prototype.run = function(){
62
      var options = this.options;
63
      var layout = this;
64

    
65
      var cy = options.cy; // cy is automatically populated for us in the constructor
66
      var eles = options.eles;
67

    
68
      var getVal = function( ele, val ){
69
        return isFunction(val) ? val.apply( ele, [ ele ] ) : val;
70
      };
71

    
72
      var bb = options.boundingBox || { x1: 0, y1: 0, w: cy.width(), h: cy.height() };
73
      if( bb.x2 === undefined ){ bb.x2 = bb.x1 + bb.w; }
74
      if( bb.w === undefined ){ bb.w = bb.x2 - bb.x1; }
75
      if( bb.y2 === undefined ){ bb.y2 = bb.y1 + bb.h; }
76
      if( bb.h === undefined ){ bb.h = bb.y2 - bb.y1; }
77

    
78
      var g = new dagre.graphlib.Graph({
79
        multigraph: true,
80
        compound: true
81
      });
82

    
83
      var gObj = {};
84
      var setGObj = function( name, val ){
85
        if( val != null ){
86
          gObj[ name ] = val;
87
        }
88
      };
89

    
90
      setGObj( 'nodesep', options.nodeSep );
91
      setGObj( 'edgesep', options.edgeSep );
92
      setGObj( 'ranksep', options.rankSep );
93
      setGObj( 'rankdir', options.rankDir );
94

    
95
      g.setGraph( gObj );
96

    
97
      g.setDefaultEdgeLabel(function() { return {}; });
98
      g.setDefaultNodeLabel(function() { return {}; });
99

    
100
      // add nodes to dagre
101
      var nodes = eles.nodes();
102
      for( var i = 0; i < nodes.length; i++ ){
103
        var node = nodes[i];
104
        var nbb = node.boundingBox();
105

    
106
        g.setNode( node.id(), {
107
          width: nbb.w,
108
          height: nbb.h,
109
          name: node.id()
110
        } );
111

    
112
        // console.log( g.node(node.id()) );
113
      }
114

    
115
      // set compound parents
116
      for( var i = 0; i < nodes.length; i++ ){
117
        var node = nodes[i];
118

    
119
        if( node.isChild() ){
120
          g.setParent( node.id(), node.parent().id() );
121
        }
122
      }
123

    
124
      // add edges to dagre
125
      var edges = eles.edges().stdFilter(function( edge ){
126
        return !edge.source().isParent() && !edge.target().isParent(); // dagre can't handle edges on compound nodes
127
      });
128
      for( var i = 0; i < edges.length; i++ ){
129
        var edge = edges[i];
130

    
131
        g.setEdge( edge.source().id(), edge.target().id(), {
132
          minlen: getVal( edge, options.minLen ),
133
          weight: getVal( edge, options.edgeWeight ),
134
          name: edge.id()
135
        }, edge.id() );
136

    
137
        // console.log( g.edge(edge.source().id(), edge.target().id(), edge.id()) );
138
      }
139

    
140
      dagre.layout( g );
141

    
142
      var gNodeIds = g.nodes();
143
      for( var i = 0; i < gNodeIds.length; i++ ){
144
        var id = gNodeIds[i];
145
        var n = g.node( id );
146

    
147
        cy.getElementById(id).scratch().dagre = n;
148
      }
149

    
150
      var dagreBB;
151

    
152
      if( options.boundingBox ){
153
        dagreBB = { x1: Infinity, x2: -Infinity, y1: Infinity, y2: -Infinity };
154
        nodes.forEach(function( node ){
155
          var dModel = node.scratch().dagre;
156

    
157
          dagreBB.x1 = Math.min( dagreBB.x1, dModel.x );
158
          dagreBB.x2 = Math.max( dagreBB.x2, dModel.x );
159

    
160
          dagreBB.y1 = Math.min( dagreBB.y1, dModel.y );
161
          dagreBB.y2 = Math.max( dagreBB.y2, dModel.y );
162
        });
163

    
164
        dagreBB.w = dagreBB.x2 - dagreBB.x1;
165
        dagreBB.h = dagreBB.y2 - dagreBB.y1;
166
      } else {
167
        dagreBB = bb;
168
      }
169

    
170
      var constrainPos = function( p ){
171
        if( options.boundingBox ){
172
          var xPct = dagreBB.w === 0 ? 0 : (p.x - dagreBB.x1) / dagreBB.w;
173
          var yPct = dagreBB.h === 0 ? 0 : (p.y - dagreBB.y1) / dagreBB.h;
174

    
175
          return {
176
            x: bb.x1 + xPct * bb.w,
177
            y: bb.y1 + yPct * bb.h
178
          };
179
        } else {
180
          return p;
181
        }
182
      };
183

    
184
      nodes.layoutPositions(layout, options, function( ele ){
185
        ele = typeof ele === "object" ? ele : this;
186
        var dModel = ele.scratch().dagre;
187

    
188
        return constrainPos({
189
          x: dModel.x,
190
          y: dModel.y
191
        });
192
      });
193

    
194
      return this; // chaining
195
    };
196

    
197
    cytoscape('layout', 'dagre', DagreLayout);
198

    
199
  };
200

    
201
  if( typeof module !== 'undefined' && module.exports ){ // expose as a commonjs module
202
    module.exports = function( cytoscape, dagre ){
203
      register( cytoscape, dagre || require('dagre') );
204
    };
205
  } else if( typeof define !== 'undefined' && define.amd ){ // expose as an amd/requirejs module
206
    define('cytoscape-dagre', function(){
207
      return register;
208
    });
209
  }
210

    
211
  if( typeof cytoscape !== 'undefined' && typeof dagre !== 'undefined' ){ // expose to global cytoscape (i.e. window.cytoscape)
212
    register( cytoscape, dagre );
213
  }
214

    
215
})();
(2-2/10)