Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | ;(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ |
michael@0 | 2 | var global=self;/** |
michael@0 | 3 | * @license |
michael@0 | 4 | * Copyright (c) 2012-2013 Chris Pettitt |
michael@0 | 5 | * |
michael@0 | 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
michael@0 | 7 | * of this software and associated documentation files (the "Software"), to deal |
michael@0 | 8 | * in the Software without restriction, including without limitation the rights |
michael@0 | 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
michael@0 | 10 | * copies of the Software, and to permit persons to whom the Software is |
michael@0 | 11 | * furnished to do so, subject to the following conditions: |
michael@0 | 12 | * |
michael@0 | 13 | * The above copyright notice and this permission notice shall be included in |
michael@0 | 14 | * all copies or substantial portions of the Software. |
michael@0 | 15 | * |
michael@0 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
michael@0 | 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
michael@0 | 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
michael@0 | 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
michael@0 | 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
michael@0 | 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
michael@0 | 22 | * THE SOFTWARE. |
michael@0 | 23 | */ |
michael@0 | 24 | global.dagreD3 = require('./index'); |
michael@0 | 25 | |
michael@0 | 26 | },{"./index":2}],2:[function(require,module,exports){ |
michael@0 | 27 | /** |
michael@0 | 28 | * @license |
michael@0 | 29 | * Copyright (c) 2012-2013 Chris Pettitt |
michael@0 | 30 | * |
michael@0 | 31 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
michael@0 | 32 | * of this software and associated documentation files (the "Software"), to deal |
michael@0 | 33 | * in the Software without restriction, including without limitation the rights |
michael@0 | 34 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
michael@0 | 35 | * copies of the Software, and to permit persons to whom the Software is |
michael@0 | 36 | * furnished to do so, subject to the following conditions: |
michael@0 | 37 | * |
michael@0 | 38 | * The above copyright notice and this permission notice shall be included in |
michael@0 | 39 | * all copies or substantial portions of the Software. |
michael@0 | 40 | * |
michael@0 | 41 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
michael@0 | 42 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
michael@0 | 43 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
michael@0 | 44 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
michael@0 | 45 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
michael@0 | 46 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
michael@0 | 47 | * THE SOFTWARE. |
michael@0 | 48 | */ |
michael@0 | 49 | module.exports = { |
michael@0 | 50 | Digraph: require('graphlib').Digraph, |
michael@0 | 51 | Renderer: require('./lib/Renderer'), |
michael@0 | 52 | json: require('graphlib').converter.json, |
michael@0 | 53 | layout: require('dagre').layout, |
michael@0 | 54 | version: require('./lib/version') |
michael@0 | 55 | }; |
michael@0 | 56 | |
michael@0 | 57 | },{"./lib/Renderer":3,"./lib/version":4,"dagre":11,"graphlib":28}],3:[function(require,module,exports){ |
michael@0 | 58 | var layout = require('dagre').layout; |
michael@0 | 59 | |
michael@0 | 60 | var d3; |
michael@0 | 61 | try { d3 = require('d3'); } catch (_) { d3 = window.d3; } |
michael@0 | 62 | |
michael@0 | 63 | module.exports = Renderer; |
michael@0 | 64 | |
michael@0 | 65 | function Renderer() { |
michael@0 | 66 | // Set up defaults... |
michael@0 | 67 | this._layout = layout(); |
michael@0 | 68 | |
michael@0 | 69 | this.drawNodes(defaultDrawNodes); |
michael@0 | 70 | this.drawEdgeLabels(defaultDrawEdgeLabels); |
michael@0 | 71 | this.drawEdgePaths(defaultDrawEdgePaths); |
michael@0 | 72 | this.positionNodes(defaultPositionNodes); |
michael@0 | 73 | this.positionEdgeLabels(defaultPositionEdgeLabels); |
michael@0 | 74 | this.positionEdgePaths(defaultPositionEdgePaths); |
michael@0 | 75 | this.transition(defaultTransition); |
michael@0 | 76 | this.postLayout(defaultPostLayout); |
michael@0 | 77 | this.postRender(defaultPostRender); |
michael@0 | 78 | |
michael@0 | 79 | this.edgeInterpolate('bundle'); |
michael@0 | 80 | this.edgeTension(0.95); |
michael@0 | 81 | } |
michael@0 | 82 | |
michael@0 | 83 | Renderer.prototype.layout = function(layout) { |
michael@0 | 84 | if (!arguments.length) { return this._layout; } |
michael@0 | 85 | this._layout = layout; |
michael@0 | 86 | return this; |
michael@0 | 87 | }; |
michael@0 | 88 | |
michael@0 | 89 | Renderer.prototype.drawNodes = function(drawNodes) { |
michael@0 | 90 | if (!arguments.length) { return this._drawNodes; } |
michael@0 | 91 | this._drawNodes = bind(drawNodes, this); |
michael@0 | 92 | return this; |
michael@0 | 93 | }; |
michael@0 | 94 | |
michael@0 | 95 | Renderer.prototype.drawEdgeLabels = function(drawEdgeLabels) { |
michael@0 | 96 | if (!arguments.length) { return this._drawEdgeLabels; } |
michael@0 | 97 | this._drawEdgeLabels = bind(drawEdgeLabels, this); |
michael@0 | 98 | return this; |
michael@0 | 99 | }; |
michael@0 | 100 | |
michael@0 | 101 | Renderer.prototype.drawEdgePaths = function(drawEdgePaths) { |
michael@0 | 102 | if (!arguments.length) { return this._drawEdgePaths; } |
michael@0 | 103 | this._drawEdgePaths = bind(drawEdgePaths, this); |
michael@0 | 104 | return this; |
michael@0 | 105 | }; |
michael@0 | 106 | |
michael@0 | 107 | Renderer.prototype.positionNodes = function(positionNodes) { |
michael@0 | 108 | if (!arguments.length) { return this._positionNodes; } |
michael@0 | 109 | this._positionNodes = bind(positionNodes, this); |
michael@0 | 110 | return this; |
michael@0 | 111 | }; |
michael@0 | 112 | |
michael@0 | 113 | Renderer.prototype.positionEdgeLabels = function(positionEdgeLabels) { |
michael@0 | 114 | if (!arguments.length) { return this._positionEdgeLabels; } |
michael@0 | 115 | this._positionEdgeLabels = bind(positionEdgeLabels, this); |
michael@0 | 116 | return this; |
michael@0 | 117 | }; |
michael@0 | 118 | |
michael@0 | 119 | Renderer.prototype.positionEdgePaths = function(positionEdgePaths) { |
michael@0 | 120 | if (!arguments.length) { return this._positionEdgePaths; } |
michael@0 | 121 | this._positionEdgePaths = bind(positionEdgePaths, this); |
michael@0 | 122 | return this; |
michael@0 | 123 | }; |
michael@0 | 124 | |
michael@0 | 125 | Renderer.prototype.transition = function(transition) { |
michael@0 | 126 | if (!arguments.length) { return this._transition; } |
michael@0 | 127 | this._transition = bind(transition, this); |
michael@0 | 128 | return this; |
michael@0 | 129 | }; |
michael@0 | 130 | |
michael@0 | 131 | Renderer.prototype.postLayout = function(postLayout) { |
michael@0 | 132 | if (!arguments.length) { return this._postLayout; } |
michael@0 | 133 | this._postLayout = bind(postLayout, this); |
michael@0 | 134 | return this; |
michael@0 | 135 | }; |
michael@0 | 136 | |
michael@0 | 137 | Renderer.prototype.postRender = function(postRender) { |
michael@0 | 138 | if (!arguments.length) { return this._postRender; } |
michael@0 | 139 | this._postRender = bind(postRender, this); |
michael@0 | 140 | return this; |
michael@0 | 141 | }; |
michael@0 | 142 | |
michael@0 | 143 | Renderer.prototype.edgeInterpolate = function(edgeInterpolate) { |
michael@0 | 144 | if (!arguments.length) { return this._edgeInterpolate; } |
michael@0 | 145 | this._edgeInterpolate = edgeInterpolate; |
michael@0 | 146 | return this; |
michael@0 | 147 | }; |
michael@0 | 148 | |
michael@0 | 149 | Renderer.prototype.edgeTension = function(edgeTension) { |
michael@0 | 150 | if (!arguments.length) { return this._edgeTension; } |
michael@0 | 151 | this._edgeTension = edgeTension; |
michael@0 | 152 | return this; |
michael@0 | 153 | }; |
michael@0 | 154 | |
michael@0 | 155 | Renderer.prototype.run = function(graph, svg) { |
michael@0 | 156 | // First copy the input graph so that it is not changed by the rendering |
michael@0 | 157 | // process. |
michael@0 | 158 | graph = copyAndInitGraph(graph); |
michael@0 | 159 | |
michael@0 | 160 | // Create layers |
michael@0 | 161 | svg |
michael@0 | 162 | .selectAll('g.edgePaths, g.edgeLabels, g.nodes') |
michael@0 | 163 | .data(['edgePaths', 'edgeLabels', 'nodes']) |
michael@0 | 164 | .enter() |
michael@0 | 165 | .append('g') |
michael@0 | 166 | .attr('class', function(d) { return d; }); |
michael@0 | 167 | |
michael@0 | 168 | |
michael@0 | 169 | // Create node and edge roots, attach labels, and capture dimension |
michael@0 | 170 | // information for use with layout. |
michael@0 | 171 | var svgNodes = this._drawNodes(graph, svg.select('g.nodes')); |
michael@0 | 172 | var svgEdgeLabels = this._drawEdgeLabels(graph, svg.select('g.edgeLabels')); |
michael@0 | 173 | |
michael@0 | 174 | svgNodes.each(function(u) { calculateDimensions(this, graph.node(u)); }); |
michael@0 | 175 | svgEdgeLabels.each(function(e) { calculateDimensions(this, graph.edge(e)); }); |
michael@0 | 176 | |
michael@0 | 177 | // Now apply the layout function |
michael@0 | 178 | var result = runLayout(graph, this._layout); |
michael@0 | 179 | |
michael@0 | 180 | // Run any user-specified post layout processing |
michael@0 | 181 | this._postLayout(result, svg); |
michael@0 | 182 | |
michael@0 | 183 | var svgEdgePaths = this._drawEdgePaths(graph, svg.select('g.edgePaths')); |
michael@0 | 184 | |
michael@0 | 185 | // Apply the layout information to the graph |
michael@0 | 186 | this._positionNodes(result, svgNodes); |
michael@0 | 187 | this._positionEdgeLabels(result, svgEdgeLabels); |
michael@0 | 188 | this._positionEdgePaths(result, svgEdgePaths); |
michael@0 | 189 | |
michael@0 | 190 | this._postRender(result, svg); |
michael@0 | 191 | |
michael@0 | 192 | return result; |
michael@0 | 193 | }; |
michael@0 | 194 | |
michael@0 | 195 | function copyAndInitGraph(graph) { |
michael@0 | 196 | var copy = graph.copy(); |
michael@0 | 197 | |
michael@0 | 198 | // Init labels if they were not present in the source graph |
michael@0 | 199 | copy.nodes().forEach(function(u) { |
michael@0 | 200 | var value = copy.node(u); |
michael@0 | 201 | if (value === undefined) { |
michael@0 | 202 | value = {}; |
michael@0 | 203 | copy.node(u, value); |
michael@0 | 204 | } |
michael@0 | 205 | if (!('label' in value)) { value.label = ''; } |
michael@0 | 206 | }); |
michael@0 | 207 | |
michael@0 | 208 | copy.edges().forEach(function(e) { |
michael@0 | 209 | var value = copy.edge(e); |
michael@0 | 210 | if (value === undefined) { |
michael@0 | 211 | value = {}; |
michael@0 | 212 | copy.edge(e, value); |
michael@0 | 213 | } |
michael@0 | 214 | if (!('label' in value)) { value.label = ''; } |
michael@0 | 215 | }); |
michael@0 | 216 | |
michael@0 | 217 | return copy; |
michael@0 | 218 | } |
michael@0 | 219 | |
michael@0 | 220 | function calculateDimensions(group, value) { |
michael@0 | 221 | var bbox = group.getBBox(); |
michael@0 | 222 | value.width = bbox.width; |
michael@0 | 223 | value.height = bbox.height; |
michael@0 | 224 | } |
michael@0 | 225 | |
michael@0 | 226 | function runLayout(graph, layout) { |
michael@0 | 227 | var result = layout.run(graph); |
michael@0 | 228 | |
michael@0 | 229 | // Copy labels to the result graph |
michael@0 | 230 | graph.eachNode(function(u, value) { result.node(u).label = value.label; }); |
michael@0 | 231 | graph.eachEdge(function(e, u, v, value) { result.edge(e).label = value.label; }); |
michael@0 | 232 | |
michael@0 | 233 | return result; |
michael@0 | 234 | } |
michael@0 | 235 | |
michael@0 | 236 | function defaultDrawNodes(g, root) { |
michael@0 | 237 | var nodes = g.nodes().filter(function(u) { return !isComposite(g, u); }); |
michael@0 | 238 | |
michael@0 | 239 | var svgNodes = root |
michael@0 | 240 | .selectAll('g.node') |
michael@0 | 241 | .classed('enter', false) |
michael@0 | 242 | .data(nodes, function(u) { return u; }); |
michael@0 | 243 | |
michael@0 | 244 | svgNodes.selectAll('*').remove(); |
michael@0 | 245 | |
michael@0 | 246 | svgNodes |
michael@0 | 247 | .enter() |
michael@0 | 248 | .append('g') |
michael@0 | 249 | .style('opacity', 0) |
michael@0 | 250 | .attr('class', 'node enter'); |
michael@0 | 251 | |
michael@0 | 252 | svgNodes.each(function(u) { addLabel(g.node(u), d3.select(this), 10, 10); }); |
michael@0 | 253 | |
michael@0 | 254 | this._transition(svgNodes.exit()) |
michael@0 | 255 | .style('opacity', 0) |
michael@0 | 256 | .remove(); |
michael@0 | 257 | |
michael@0 | 258 | return svgNodes; |
michael@0 | 259 | } |
michael@0 | 260 | |
michael@0 | 261 | function defaultDrawEdgeLabels(g, root) { |
michael@0 | 262 | var svgEdgeLabels = root |
michael@0 | 263 | .selectAll('g.edgeLabel') |
michael@0 | 264 | .classed('enter', false) |
michael@0 | 265 | .data(g.edges(), function (e) { return e; }); |
michael@0 | 266 | |
michael@0 | 267 | svgEdgeLabels.selectAll('*').remove(); |
michael@0 | 268 | |
michael@0 | 269 | svgEdgeLabels |
michael@0 | 270 | .enter() |
michael@0 | 271 | .append('g') |
michael@0 | 272 | .style('opacity', 0) |
michael@0 | 273 | .attr('class', 'edgeLabel enter'); |
michael@0 | 274 | |
michael@0 | 275 | svgEdgeLabels.each(function(e) { addLabel(g.edge(e), d3.select(this), 0, 0); }); |
michael@0 | 276 | |
michael@0 | 277 | this._transition(svgEdgeLabels.exit()) |
michael@0 | 278 | .style('opacity', 0) |
michael@0 | 279 | .remove(); |
michael@0 | 280 | |
michael@0 | 281 | return svgEdgeLabels; |
michael@0 | 282 | } |
michael@0 | 283 | |
michael@0 | 284 | var defaultDrawEdgePaths = function(g, root) { |
michael@0 | 285 | var svgEdgePaths = root |
michael@0 | 286 | .selectAll('g.edgePath') |
michael@0 | 287 | .classed('enter', false) |
michael@0 | 288 | .data(g.edges(), function(e) { return e; }); |
michael@0 | 289 | |
michael@0 | 290 | svgEdgePaths |
michael@0 | 291 | .enter() |
michael@0 | 292 | .append('g') |
michael@0 | 293 | .attr('class', 'edgePath enter') |
michael@0 | 294 | .append('path') |
michael@0 | 295 | .style('opacity', 0) |
michael@0 | 296 | .attr('marker-end', 'url(#arrowhead)'); |
michael@0 | 297 | |
michael@0 | 298 | this._transition(svgEdgePaths.exit()) |
michael@0 | 299 | .style('opacity', 0) |
michael@0 | 300 | .remove(); |
michael@0 | 301 | |
michael@0 | 302 | return svgEdgePaths; |
michael@0 | 303 | }; |
michael@0 | 304 | |
michael@0 | 305 | function defaultPositionNodes(g, svgNodes, svgNodesEnter) { |
michael@0 | 306 | function transform(u) { |
michael@0 | 307 | var value = g.node(u); |
michael@0 | 308 | return 'translate(' + value.x + ',' + value.y + ')'; |
michael@0 | 309 | } |
michael@0 | 310 | |
michael@0 | 311 | // For entering nodes, position immediately without transition |
michael@0 | 312 | svgNodes.filter('.enter').attr('transform', transform); |
michael@0 | 313 | |
michael@0 | 314 | this._transition(svgNodes) |
michael@0 | 315 | .style('opacity', 1) |
michael@0 | 316 | .attr('transform', transform); |
michael@0 | 317 | } |
michael@0 | 318 | |
michael@0 | 319 | function defaultPositionEdgeLabels(g, svgEdgeLabels) { |
michael@0 | 320 | function transform(e) { |
michael@0 | 321 | var value = g.edge(e); |
michael@0 | 322 | var point = findMidPoint(value.points); |
michael@0 | 323 | return 'translate(' + point.x + ',' + point.y + ')'; |
michael@0 | 324 | } |
michael@0 | 325 | |
michael@0 | 326 | // For entering edge labels, position immediately without transition |
michael@0 | 327 | svgEdgeLabels.filter('.enter').attr('transform', transform); |
michael@0 | 328 | |
michael@0 | 329 | this._transition(svgEdgeLabels) |
michael@0 | 330 | .style('opacity', 1) |
michael@0 | 331 | .attr('transform', transform); |
michael@0 | 332 | } |
michael@0 | 333 | |
michael@0 | 334 | function defaultPositionEdgePaths(g, svgEdgePaths) { |
michael@0 | 335 | var interpolate = this._edgeInterpolate, |
michael@0 | 336 | tension = this._edgeTension; |
michael@0 | 337 | |
michael@0 | 338 | function calcPoints(e) { |
michael@0 | 339 | var value = g.edge(e); |
michael@0 | 340 | var source = g.node(g.incidentNodes(e)[0]); |
michael@0 | 341 | var target = g.node(g.incidentNodes(e)[1]); |
michael@0 | 342 | var points = value.points.slice(); |
michael@0 | 343 | |
michael@0 | 344 | var p0 = points.length === 0 ? target : points[0]; |
michael@0 | 345 | var p1 = points.length === 0 ? source : points[points.length - 1]; |
michael@0 | 346 | |
michael@0 | 347 | points.unshift(intersectRect(source, p0)); |
michael@0 | 348 | // TODO: use bpodgursky's shortening algorithm here |
michael@0 | 349 | points.push(intersectRect(target, p1)); |
michael@0 | 350 | |
michael@0 | 351 | return d3.svg.line() |
michael@0 | 352 | .x(function(d) { return d.x; }) |
michael@0 | 353 | .y(function(d) { return d.y; }) |
michael@0 | 354 | .interpolate(interpolate) |
michael@0 | 355 | .tension(tension) |
michael@0 | 356 | (points); |
michael@0 | 357 | } |
michael@0 | 358 | |
michael@0 | 359 | svgEdgePaths.filter('.enter').selectAll('path') |
michael@0 | 360 | .attr('d', calcPoints); |
michael@0 | 361 | |
michael@0 | 362 | this._transition(svgEdgePaths.selectAll('path')) |
michael@0 | 363 | .attr('d', calcPoints) |
michael@0 | 364 | .style('opacity', 1); |
michael@0 | 365 | } |
michael@0 | 366 | |
michael@0 | 367 | // By default we do not use transitions |
michael@0 | 368 | function defaultTransition(selection) { |
michael@0 | 369 | return selection; |
michael@0 | 370 | } |
michael@0 | 371 | |
michael@0 | 372 | function defaultPostLayout() { |
michael@0 | 373 | // Do nothing |
michael@0 | 374 | } |
michael@0 | 375 | |
michael@0 | 376 | function defaultPostRender(graph, root) { |
michael@0 | 377 | if (graph.isDirected() && root.select('#arrowhead').empty()) { |
michael@0 | 378 | root |
michael@0 | 379 | .append('svg:defs') |
michael@0 | 380 | .append('svg:marker') |
michael@0 | 381 | .attr('id', 'arrowhead') |
michael@0 | 382 | .attr('viewBox', '0 0 10 10') |
michael@0 | 383 | .attr('refX', 8) |
michael@0 | 384 | .attr('refY', 5) |
michael@0 | 385 | .attr('markerUnits', 'strokewidth') |
michael@0 | 386 | .attr('markerWidth', 8) |
michael@0 | 387 | .attr('markerHeight', 5) |
michael@0 | 388 | .attr('orient', 'auto') |
michael@0 | 389 | .attr('style', 'fill: #333') |
michael@0 | 390 | .append('svg:path') |
michael@0 | 391 | .attr('d', 'M 0 0 L 10 5 L 0 10 z'); |
michael@0 | 392 | } |
michael@0 | 393 | } |
michael@0 | 394 | |
michael@0 | 395 | function addLabel(node, root, marginX, marginY) { |
michael@0 | 396 | // Add the rect first so that it appears behind the label |
michael@0 | 397 | var label = node.label; |
michael@0 | 398 | var rect = root.append('rect'); |
michael@0 | 399 | var labelSvg = root.append('g'); |
michael@0 | 400 | |
michael@0 | 401 | if (label[0] === '<') { |
michael@0 | 402 | addForeignObjectLabel(label, labelSvg); |
michael@0 | 403 | // No margin for HTML elements |
michael@0 | 404 | marginX = marginY = 0; |
michael@0 | 405 | } else { |
michael@0 | 406 | addTextLabel(label, |
michael@0 | 407 | labelSvg, |
michael@0 | 408 | Math.floor(node.labelCols), |
michael@0 | 409 | node.labelCut); |
michael@0 | 410 | } |
michael@0 | 411 | |
michael@0 | 412 | var bbox = root.node().getBBox(); |
michael@0 | 413 | |
michael@0 | 414 | labelSvg.attr('transform', |
michael@0 | 415 | 'translate(' + (-bbox.width / 2) + ',' + (-bbox.height / 2) + ')'); |
michael@0 | 416 | |
michael@0 | 417 | rect |
michael@0 | 418 | .attr('rx', 5) |
michael@0 | 419 | .attr('ry', 5) |
michael@0 | 420 | .attr('x', -(bbox.width / 2 + marginX)) |
michael@0 | 421 | .attr('y', -(bbox.height / 2 + marginY)) |
michael@0 | 422 | .attr('width', bbox.width + 2 * marginX) |
michael@0 | 423 | .attr('height', bbox.height + 2 * marginY); |
michael@0 | 424 | } |
michael@0 | 425 | |
michael@0 | 426 | function addForeignObjectLabel(label, root) { |
michael@0 | 427 | var fo = root |
michael@0 | 428 | .append('foreignObject') |
michael@0 | 429 | .attr('width', '100000'); |
michael@0 | 430 | |
michael@0 | 431 | var w, h; |
michael@0 | 432 | fo |
michael@0 | 433 | .append('xhtml:div') |
michael@0 | 434 | .style('float', 'left') |
michael@0 | 435 | // TODO find a better way to get dimensions for foreignObjects... |
michael@0 | 436 | .html(function() { return label; }) |
michael@0 | 437 | .each(function() { |
michael@0 | 438 | w = this.clientWidth; |
michael@0 | 439 | h = this.clientHeight; |
michael@0 | 440 | }); |
michael@0 | 441 | |
michael@0 | 442 | fo |
michael@0 | 443 | .attr('width', w) |
michael@0 | 444 | .attr('height', h); |
michael@0 | 445 | } |
michael@0 | 446 | |
michael@0 | 447 | function addTextLabel(label, root, labelCols, labelCut) { |
michael@0 | 448 | if (labelCut === undefined) labelCut = "false"; |
michael@0 | 449 | labelCut = (labelCut.toString().toLowerCase() === "true"); |
michael@0 | 450 | |
michael@0 | 451 | var node = root |
michael@0 | 452 | .append('text') |
michael@0 | 453 | .attr('text-anchor', 'left'); |
michael@0 | 454 | |
michael@0 | 455 | label = label.replace(/\\n/g, "\n"); |
michael@0 | 456 | |
michael@0 | 457 | var arr = labelCols ? wordwrap(label, labelCols, labelCut) : label; |
michael@0 | 458 | arr = arr.split("\n"); |
michael@0 | 459 | for (var i = 0; i < arr.length; i++) { |
michael@0 | 460 | node |
michael@0 | 461 | .append('tspan') |
michael@0 | 462 | .attr('dy', '1em') |
michael@0 | 463 | .attr('x', '1') |
michael@0 | 464 | .text(arr[i]); |
michael@0 | 465 | } |
michael@0 | 466 | } |
michael@0 | 467 | |
michael@0 | 468 | // Thanks to |
michael@0 | 469 | // http://james.padolsey.com/javascript/wordwrap-for-javascript/ |
michael@0 | 470 | function wordwrap (str, width, cut, brk) { |
michael@0 | 471 | brk = brk || '\n'; |
michael@0 | 472 | width = width || 75; |
michael@0 | 473 | cut = cut || false; |
michael@0 | 474 | |
michael@0 | 475 | if (!str) { return str; } |
michael@0 | 476 | |
michael@0 | 477 | var regex = '.{1,' +width+ '}(\\s|$)' + (cut ? '|.{' +width+ '}|.+$' : '|\\S+?(\\s|$)'); |
michael@0 | 478 | |
michael@0 | 479 | return str.match( RegExp(regex, 'g') ).join( brk ); |
michael@0 | 480 | } |
michael@0 | 481 | |
michael@0 | 482 | function findMidPoint(points) { |
michael@0 | 483 | var midIdx = points.length / 2; |
michael@0 | 484 | if (points.length % 2) { |
michael@0 | 485 | return points[Math.floor(midIdx)]; |
michael@0 | 486 | } else { |
michael@0 | 487 | var p0 = points[midIdx - 1]; |
michael@0 | 488 | var p1 = points[midIdx]; |
michael@0 | 489 | return {x: (p0.x + p1.x) / 2, y: (p0.y + p1.y) / 2}; |
michael@0 | 490 | } |
michael@0 | 491 | } |
michael@0 | 492 | |
michael@0 | 493 | function intersectRect(rect, point) { |
michael@0 | 494 | var x = rect.x; |
michael@0 | 495 | var y = rect.y; |
michael@0 | 496 | |
michael@0 | 497 | // For now we only support rectangles |
michael@0 | 498 | |
michael@0 | 499 | // Rectangle intersection algorithm from: |
michael@0 | 500 | // http://math.stackexchange.com/questions/108113/find-edge-between-two-boxes |
michael@0 | 501 | var dx = point.x - x; |
michael@0 | 502 | var dy = point.y - y; |
michael@0 | 503 | var w = rect.width / 2; |
michael@0 | 504 | var h = rect.height / 2; |
michael@0 | 505 | |
michael@0 | 506 | var sx, sy; |
michael@0 | 507 | if (Math.abs(dy) * w > Math.abs(dx) * h) { |
michael@0 | 508 | // Intersection is top or bottom of rect. |
michael@0 | 509 | if (dy < 0) { |
michael@0 | 510 | h = -h; |
michael@0 | 511 | } |
michael@0 | 512 | sx = dy === 0 ? 0 : h * dx / dy; |
michael@0 | 513 | sy = h; |
michael@0 | 514 | } else { |
michael@0 | 515 | // Intersection is left or right of rect. |
michael@0 | 516 | if (dx < 0) { |
michael@0 | 517 | w = -w; |
michael@0 | 518 | } |
michael@0 | 519 | sx = w; |
michael@0 | 520 | sy = dx === 0 ? 0 : w * dy / dx; |
michael@0 | 521 | } |
michael@0 | 522 | |
michael@0 | 523 | return {x: x + sx, y: y + sy}; |
michael@0 | 524 | } |
michael@0 | 525 | |
michael@0 | 526 | function isComposite(g, u) { |
michael@0 | 527 | return 'children' in g && g.children(u).length; |
michael@0 | 528 | } |
michael@0 | 529 | |
michael@0 | 530 | function bind(func, thisArg) { |
michael@0 | 531 | // For some reason PhantomJS occassionally fails when using the builtin bind, |
michael@0 | 532 | // so we check if it is available and if not, use a degenerate polyfill. |
michael@0 | 533 | if (func.bind) { |
michael@0 | 534 | return func.bind(thisArg); |
michael@0 | 535 | } |
michael@0 | 536 | |
michael@0 | 537 | return function() { |
michael@0 | 538 | return func.apply(thisArg, arguments); |
michael@0 | 539 | }; |
michael@0 | 540 | } |
michael@0 | 541 | |
michael@0 | 542 | },{"d3":10,"dagre":11}],4:[function(require,module,exports){ |
michael@0 | 543 | module.exports = '0.1.5'; |
michael@0 | 544 | |
michael@0 | 545 | },{}],5:[function(require,module,exports){ |
michael@0 | 546 | exports.Set = require('./lib/Set'); |
michael@0 | 547 | exports.PriorityQueue = require('./lib/PriorityQueue'); |
michael@0 | 548 | exports.version = require('./lib/version'); |
michael@0 | 549 | |
michael@0 | 550 | },{"./lib/PriorityQueue":6,"./lib/Set":7,"./lib/version":9}],6:[function(require,module,exports){ |
michael@0 | 551 | module.exports = PriorityQueue; |
michael@0 | 552 | |
michael@0 | 553 | /** |
michael@0 | 554 | * A min-priority queue data structure. This algorithm is derived from Cormen, |
michael@0 | 555 | * et al., "Introduction to Algorithms". The basic idea of a min-priority |
michael@0 | 556 | * queue is that you can efficiently (in O(1) time) get the smallest key in |
michael@0 | 557 | * the queue. Adding and removing elements takes O(log n) time. A key can |
michael@0 | 558 | * have its priority decreased in O(log n) time. |
michael@0 | 559 | */ |
michael@0 | 560 | function PriorityQueue() { |
michael@0 | 561 | this._arr = []; |
michael@0 | 562 | this._keyIndices = {}; |
michael@0 | 563 | } |
michael@0 | 564 | |
michael@0 | 565 | /** |
michael@0 | 566 | * Returns the number of elements in the queue. Takes `O(1)` time. |
michael@0 | 567 | */ |
michael@0 | 568 | PriorityQueue.prototype.size = function() { |
michael@0 | 569 | return this._arr.length; |
michael@0 | 570 | }; |
michael@0 | 571 | |
michael@0 | 572 | /** |
michael@0 | 573 | * Returns the keys that are in the queue. Takes `O(n)` time. |
michael@0 | 574 | */ |
michael@0 | 575 | PriorityQueue.prototype.keys = function() { |
michael@0 | 576 | return this._arr.map(function(x) { return x.key; }); |
michael@0 | 577 | }; |
michael@0 | 578 | |
michael@0 | 579 | /** |
michael@0 | 580 | * Returns `true` if **key** is in the queue and `false` if not. |
michael@0 | 581 | */ |
michael@0 | 582 | PriorityQueue.prototype.has = function(key) { |
michael@0 | 583 | return key in this._keyIndices; |
michael@0 | 584 | }; |
michael@0 | 585 | |
michael@0 | 586 | /** |
michael@0 | 587 | * Returns the priority for **key**. If **key** is not present in the queue |
michael@0 | 588 | * then this function returns `undefined`. Takes `O(1)` time. |
michael@0 | 589 | * |
michael@0 | 590 | * @param {Object} key |
michael@0 | 591 | */ |
michael@0 | 592 | PriorityQueue.prototype.priority = function(key) { |
michael@0 | 593 | var index = this._keyIndices[key]; |
michael@0 | 594 | if (index !== undefined) { |
michael@0 | 595 | return this._arr[index].priority; |
michael@0 | 596 | } |
michael@0 | 597 | }; |
michael@0 | 598 | |
michael@0 | 599 | /** |
michael@0 | 600 | * Returns the key for the minimum element in this queue. If the queue is |
michael@0 | 601 | * empty this function throws an Error. Takes `O(1)` time. |
michael@0 | 602 | */ |
michael@0 | 603 | PriorityQueue.prototype.min = function() { |
michael@0 | 604 | if (this.size() === 0) { |
michael@0 | 605 | throw new Error("Queue underflow"); |
michael@0 | 606 | } |
michael@0 | 607 | return this._arr[0].key; |
michael@0 | 608 | }; |
michael@0 | 609 | |
michael@0 | 610 | /** |
michael@0 | 611 | * Inserts a new key into the priority queue. If the key already exists in |
michael@0 | 612 | * the queue this function returns `false`; otherwise it will return `true`. |
michael@0 | 613 | * Takes `O(n)` time. |
michael@0 | 614 | * |
michael@0 | 615 | * @param {Object} key the key to add |
michael@0 | 616 | * @param {Number} priority the initial priority for the key |
michael@0 | 617 | */ |
michael@0 | 618 | PriorityQueue.prototype.add = function(key, priority) { |
michael@0 | 619 | var keyIndices = this._keyIndices; |
michael@0 | 620 | if (!(key in keyIndices)) { |
michael@0 | 621 | var arr = this._arr; |
michael@0 | 622 | var index = arr.length; |
michael@0 | 623 | keyIndices[key] = index; |
michael@0 | 624 | arr.push({key: key, priority: priority}); |
michael@0 | 625 | this._decrease(index); |
michael@0 | 626 | return true; |
michael@0 | 627 | } |
michael@0 | 628 | return false; |
michael@0 | 629 | }; |
michael@0 | 630 | |
michael@0 | 631 | /** |
michael@0 | 632 | * Removes and returns the smallest key in the queue. Takes `O(log n)` time. |
michael@0 | 633 | */ |
michael@0 | 634 | PriorityQueue.prototype.removeMin = function() { |
michael@0 | 635 | this._swap(0, this._arr.length - 1); |
michael@0 | 636 | var min = this._arr.pop(); |
michael@0 | 637 | delete this._keyIndices[min.key]; |
michael@0 | 638 | this._heapify(0); |
michael@0 | 639 | return min.key; |
michael@0 | 640 | }; |
michael@0 | 641 | |
michael@0 | 642 | /** |
michael@0 | 643 | * Decreases the priority for **key** to **priority**. If the new priority is |
michael@0 | 644 | * greater than the previous priority, this function will throw an Error. |
michael@0 | 645 | * |
michael@0 | 646 | * @param {Object} key the key for which to raise priority |
michael@0 | 647 | * @param {Number} priority the new priority for the key |
michael@0 | 648 | */ |
michael@0 | 649 | PriorityQueue.prototype.decrease = function(key, priority) { |
michael@0 | 650 | var index = this._keyIndices[key]; |
michael@0 | 651 | if (priority > this._arr[index].priority) { |
michael@0 | 652 | throw new Error("New priority is greater than current priority. " + |
michael@0 | 653 | "Key: " + key + " Old: " + this._arr[index].priority + " New: " + priority); |
michael@0 | 654 | } |
michael@0 | 655 | this._arr[index].priority = priority; |
michael@0 | 656 | this._decrease(index); |
michael@0 | 657 | }; |
michael@0 | 658 | |
michael@0 | 659 | PriorityQueue.prototype._heapify = function(i) { |
michael@0 | 660 | var arr = this._arr; |
michael@0 | 661 | var l = 2 * i, |
michael@0 | 662 | r = l + 1, |
michael@0 | 663 | largest = i; |
michael@0 | 664 | if (l < arr.length) { |
michael@0 | 665 | largest = arr[l].priority < arr[largest].priority ? l : largest; |
michael@0 | 666 | if (r < arr.length) { |
michael@0 | 667 | largest = arr[r].priority < arr[largest].priority ? r : largest; |
michael@0 | 668 | } |
michael@0 | 669 | if (largest !== i) { |
michael@0 | 670 | this._swap(i, largest); |
michael@0 | 671 | this._heapify(largest); |
michael@0 | 672 | } |
michael@0 | 673 | } |
michael@0 | 674 | }; |
michael@0 | 675 | |
michael@0 | 676 | PriorityQueue.prototype._decrease = function(index) { |
michael@0 | 677 | var arr = this._arr; |
michael@0 | 678 | var priority = arr[index].priority; |
michael@0 | 679 | var parent; |
michael@0 | 680 | while (index !== 0) { |
michael@0 | 681 | parent = index >> 1; |
michael@0 | 682 | if (arr[parent].priority < priority) { |
michael@0 | 683 | break; |
michael@0 | 684 | } |
michael@0 | 685 | this._swap(index, parent); |
michael@0 | 686 | index = parent; |
michael@0 | 687 | } |
michael@0 | 688 | }; |
michael@0 | 689 | |
michael@0 | 690 | PriorityQueue.prototype._swap = function(i, j) { |
michael@0 | 691 | var arr = this._arr; |
michael@0 | 692 | var keyIndices = this._keyIndices; |
michael@0 | 693 | var origArrI = arr[i]; |
michael@0 | 694 | var origArrJ = arr[j]; |
michael@0 | 695 | arr[i] = origArrJ; |
michael@0 | 696 | arr[j] = origArrI; |
michael@0 | 697 | keyIndices[origArrJ.key] = i; |
michael@0 | 698 | keyIndices[origArrI.key] = j; |
michael@0 | 699 | }; |
michael@0 | 700 | |
michael@0 | 701 | },{}],7:[function(require,module,exports){ |
michael@0 | 702 | var util = require('./util'); |
michael@0 | 703 | |
michael@0 | 704 | module.exports = Set; |
michael@0 | 705 | |
michael@0 | 706 | /** |
michael@0 | 707 | * Constructs a new Set with an optional set of `initialKeys`. |
michael@0 | 708 | * |
michael@0 | 709 | * It is important to note that keys are coerced to String for most purposes |
michael@0 | 710 | * with this object, similar to the behavior of JavaScript's Object. For |
michael@0 | 711 | * example, the following will add only one key: |
michael@0 | 712 | * |
michael@0 | 713 | * var s = new Set(); |
michael@0 | 714 | * s.add(1); |
michael@0 | 715 | * s.add("1"); |
michael@0 | 716 | * |
michael@0 | 717 | * However, the type of the key is preserved internally so that `keys` returns |
michael@0 | 718 | * the original key set uncoerced. For the above example, `keys` would return |
michael@0 | 719 | * `[1]`. |
michael@0 | 720 | */ |
michael@0 | 721 | function Set(initialKeys) { |
michael@0 | 722 | this._size = 0; |
michael@0 | 723 | this._keys = {}; |
michael@0 | 724 | |
michael@0 | 725 | if (initialKeys) { |
michael@0 | 726 | for (var i = 0, il = initialKeys.length; i < il; ++i) { |
michael@0 | 727 | this.add(initialKeys[i]); |
michael@0 | 728 | } |
michael@0 | 729 | } |
michael@0 | 730 | } |
michael@0 | 731 | |
michael@0 | 732 | /** |
michael@0 | 733 | * Returns a new Set that represents the set intersection of the array of given |
michael@0 | 734 | * sets. |
michael@0 | 735 | */ |
michael@0 | 736 | Set.intersect = function(sets) { |
michael@0 | 737 | if (sets.length === 0) { |
michael@0 | 738 | return new Set(); |
michael@0 | 739 | } |
michael@0 | 740 | |
michael@0 | 741 | var result = new Set(!util.isArray(sets[0]) ? sets[0].keys() : sets[0]); |
michael@0 | 742 | for (var i = 1, il = sets.length; i < il; ++i) { |
michael@0 | 743 | var resultKeys = result.keys(), |
michael@0 | 744 | other = !util.isArray(sets[i]) ? sets[i] : new Set(sets[i]); |
michael@0 | 745 | for (var j = 0, jl = resultKeys.length; j < jl; ++j) { |
michael@0 | 746 | var key = resultKeys[j]; |
michael@0 | 747 | if (!other.has(key)) { |
michael@0 | 748 | result.remove(key); |
michael@0 | 749 | } |
michael@0 | 750 | } |
michael@0 | 751 | } |
michael@0 | 752 | |
michael@0 | 753 | return result; |
michael@0 | 754 | }; |
michael@0 | 755 | |
michael@0 | 756 | /** |
michael@0 | 757 | * Returns a new Set that represents the set union of the array of given sets. |
michael@0 | 758 | */ |
michael@0 | 759 | Set.union = function(sets) { |
michael@0 | 760 | var totalElems = util.reduce(sets, function(lhs, rhs) { |
michael@0 | 761 | return lhs + (rhs.size ? rhs.size() : rhs.length); |
michael@0 | 762 | }, 0); |
michael@0 | 763 | var arr = new Array(totalElems); |
michael@0 | 764 | |
michael@0 | 765 | var k = 0; |
michael@0 | 766 | for (var i = 0, il = sets.length; i < il; ++i) { |
michael@0 | 767 | var cur = sets[i], |
michael@0 | 768 | keys = !util.isArray(cur) ? cur.keys() : cur; |
michael@0 | 769 | for (var j = 0, jl = keys.length; j < jl; ++j) { |
michael@0 | 770 | arr[k++] = keys[j]; |
michael@0 | 771 | } |
michael@0 | 772 | } |
michael@0 | 773 | |
michael@0 | 774 | return new Set(arr); |
michael@0 | 775 | }; |
michael@0 | 776 | |
michael@0 | 777 | /** |
michael@0 | 778 | * Returns the size of this set in `O(1)` time. |
michael@0 | 779 | */ |
michael@0 | 780 | Set.prototype.size = function() { |
michael@0 | 781 | return this._size; |
michael@0 | 782 | }; |
michael@0 | 783 | |
michael@0 | 784 | /** |
michael@0 | 785 | * Returns the keys in this set. Takes `O(n)` time. |
michael@0 | 786 | */ |
michael@0 | 787 | Set.prototype.keys = function() { |
michael@0 | 788 | return values(this._keys); |
michael@0 | 789 | }; |
michael@0 | 790 | |
michael@0 | 791 | /** |
michael@0 | 792 | * Tests if a key is present in this Set. Returns `true` if it is and `false` |
michael@0 | 793 | * if not. Takes `O(1)` time. |
michael@0 | 794 | */ |
michael@0 | 795 | Set.prototype.has = function(key) { |
michael@0 | 796 | return key in this._keys; |
michael@0 | 797 | }; |
michael@0 | 798 | |
michael@0 | 799 | /** |
michael@0 | 800 | * Adds a new key to this Set if it is not already present. Returns `true` if |
michael@0 | 801 | * the key was added and `false` if it was already present. Takes `O(1)` time. |
michael@0 | 802 | */ |
michael@0 | 803 | Set.prototype.add = function(key) { |
michael@0 | 804 | if (!(key in this._keys)) { |
michael@0 | 805 | this._keys[key] = key; |
michael@0 | 806 | ++this._size; |
michael@0 | 807 | return true; |
michael@0 | 808 | } |
michael@0 | 809 | return false; |
michael@0 | 810 | }; |
michael@0 | 811 | |
michael@0 | 812 | /** |
michael@0 | 813 | * Removes a key from this Set. If the key was removed this function returns |
michael@0 | 814 | * `true`. If not, it returns `false`. Takes `O(1)` time. |
michael@0 | 815 | */ |
michael@0 | 816 | Set.prototype.remove = function(key) { |
michael@0 | 817 | if (key in this._keys) { |
michael@0 | 818 | delete this._keys[key]; |
michael@0 | 819 | --this._size; |
michael@0 | 820 | return true; |
michael@0 | 821 | } |
michael@0 | 822 | return false; |
michael@0 | 823 | }; |
michael@0 | 824 | |
michael@0 | 825 | /* |
michael@0 | 826 | * Returns an array of all values for properties of **o**. |
michael@0 | 827 | */ |
michael@0 | 828 | function values(o) { |
michael@0 | 829 | var ks = Object.keys(o), |
michael@0 | 830 | len = ks.length, |
michael@0 | 831 | result = new Array(len), |
michael@0 | 832 | i; |
michael@0 | 833 | for (i = 0; i < len; ++i) { |
michael@0 | 834 | result[i] = o[ks[i]]; |
michael@0 | 835 | } |
michael@0 | 836 | return result; |
michael@0 | 837 | } |
michael@0 | 838 | |
michael@0 | 839 | },{"./util":8}],8:[function(require,module,exports){ |
michael@0 | 840 | /* |
michael@0 | 841 | * This polyfill comes from |
michael@0 | 842 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray |
michael@0 | 843 | */ |
michael@0 | 844 | if(!Array.isArray) { |
michael@0 | 845 | exports.isArray = function (vArg) { |
michael@0 | 846 | return Object.prototype.toString.call(vArg) === '[object Array]'; |
michael@0 | 847 | }; |
michael@0 | 848 | } else { |
michael@0 | 849 | exports.isArray = Array.isArray; |
michael@0 | 850 | } |
michael@0 | 851 | |
michael@0 | 852 | /* |
michael@0 | 853 | * Slightly adapted polyfill from |
michael@0 | 854 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce |
michael@0 | 855 | */ |
michael@0 | 856 | if ('function' !== typeof Array.prototype.reduce) { |
michael@0 | 857 | exports.reduce = function(array, callback, opt_initialValue) { |
michael@0 | 858 | 'use strict'; |
michael@0 | 859 | if (null === array || 'undefined' === typeof array) { |
michael@0 | 860 | // At the moment all modern browsers, that support strict mode, have |
michael@0 | 861 | // native implementation of Array.prototype.reduce. For instance, IE8 |
michael@0 | 862 | // does not support strict mode, so this check is actually useless. |
michael@0 | 863 | throw new TypeError( |
michael@0 | 864 | 'Array.prototype.reduce called on null or undefined'); |
michael@0 | 865 | } |
michael@0 | 866 | if ('function' !== typeof callback) { |
michael@0 | 867 | throw new TypeError(callback + ' is not a function'); |
michael@0 | 868 | } |
michael@0 | 869 | var index, value, |
michael@0 | 870 | length = array.length >>> 0, |
michael@0 | 871 | isValueSet = false; |
michael@0 | 872 | if (1 < arguments.length) { |
michael@0 | 873 | value = opt_initialValue; |
michael@0 | 874 | isValueSet = true; |
michael@0 | 875 | } |
michael@0 | 876 | for (index = 0; length > index; ++index) { |
michael@0 | 877 | if (array.hasOwnProperty(index)) { |
michael@0 | 878 | if (isValueSet) { |
michael@0 | 879 | value = callback(value, array[index], index, array); |
michael@0 | 880 | } |
michael@0 | 881 | else { |
michael@0 | 882 | value = array[index]; |
michael@0 | 883 | isValueSet = true; |
michael@0 | 884 | } |
michael@0 | 885 | } |
michael@0 | 886 | } |
michael@0 | 887 | if (!isValueSet) { |
michael@0 | 888 | throw new TypeError('Reduce of empty array with no initial value'); |
michael@0 | 889 | } |
michael@0 | 890 | return value; |
michael@0 | 891 | }; |
michael@0 | 892 | } else { |
michael@0 | 893 | exports.reduce = function(array, callback, opt_initialValue) { |
michael@0 | 894 | return array.reduce(callback, opt_initialValue); |
michael@0 | 895 | }; |
michael@0 | 896 | } |
michael@0 | 897 | |
michael@0 | 898 | },{}],9:[function(require,module,exports){ |
michael@0 | 899 | module.exports = '1.1.3'; |
michael@0 | 900 | |
michael@0 | 901 | },{}],10:[function(require,module,exports){ |
michael@0 | 902 | require("./d3"); |
michael@0 | 903 | module.exports = d3; |
michael@0 | 904 | (function () { delete this.d3; })(); // unset global |
michael@0 | 905 | |
michael@0 | 906 | },{}],11:[function(require,module,exports){ |
michael@0 | 907 | /* |
michael@0 | 908 | Copyright (c) 2012-2013 Chris Pettitt |
michael@0 | 909 | |
michael@0 | 910 | Permission is hereby granted, free of charge, to any person obtaining a copy |
michael@0 | 911 | of this software and associated documentation files (the "Software"), to deal |
michael@0 | 912 | in the Software without restriction, including without limitation the rights |
michael@0 | 913 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
michael@0 | 914 | copies of the Software, and to permit persons to whom the Software is |
michael@0 | 915 | furnished to do so, subject to the following conditions: |
michael@0 | 916 | |
michael@0 | 917 | The above copyright notice and this permission notice shall be included in |
michael@0 | 918 | all copies or substantial portions of the Software. |
michael@0 | 919 | |
michael@0 | 920 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
michael@0 | 921 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
michael@0 | 922 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
michael@0 | 923 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
michael@0 | 924 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
michael@0 | 925 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
michael@0 | 926 | THE SOFTWARE. |
michael@0 | 927 | */ |
michael@0 | 928 | exports.Digraph = require("graphlib").Digraph; |
michael@0 | 929 | exports.Graph = require("graphlib").Graph; |
michael@0 | 930 | exports.layout = require("./lib/layout"); |
michael@0 | 931 | exports.version = require("./lib/version"); |
michael@0 | 932 | |
michael@0 | 933 | },{"./lib/layout":12,"./lib/version":27,"graphlib":28}],12:[function(require,module,exports){ |
michael@0 | 934 | var util = require('./util'), |
michael@0 | 935 | rank = require('./rank'), |
michael@0 | 936 | order = require('./order'), |
michael@0 | 937 | CGraph = require('graphlib').CGraph, |
michael@0 | 938 | CDigraph = require('graphlib').CDigraph; |
michael@0 | 939 | |
michael@0 | 940 | module.exports = function() { |
michael@0 | 941 | // External configuration |
michael@0 | 942 | var config = { |
michael@0 | 943 | // How much debug information to include? |
michael@0 | 944 | debugLevel: 0, |
michael@0 | 945 | // Max number of sweeps to perform in order phase |
michael@0 | 946 | orderMaxSweeps: order.DEFAULT_MAX_SWEEPS, |
michael@0 | 947 | // Use network simplex algorithm in ranking |
michael@0 | 948 | rankSimplex: false, |
michael@0 | 949 | // Rank direction. Valid values are (TB, LR) |
michael@0 | 950 | rankDir: 'TB' |
michael@0 | 951 | }; |
michael@0 | 952 | |
michael@0 | 953 | // Phase functions |
michael@0 | 954 | var position = require('./position')(); |
michael@0 | 955 | |
michael@0 | 956 | // This layout object |
michael@0 | 957 | var self = {}; |
michael@0 | 958 | |
michael@0 | 959 | self.orderIters = util.propertyAccessor(self, config, 'orderMaxSweeps'); |
michael@0 | 960 | |
michael@0 | 961 | self.rankSimplex = util.propertyAccessor(self, config, 'rankSimplex'); |
michael@0 | 962 | |
michael@0 | 963 | self.nodeSep = delegateProperty(position.nodeSep); |
michael@0 | 964 | self.edgeSep = delegateProperty(position.edgeSep); |
michael@0 | 965 | self.universalSep = delegateProperty(position.universalSep); |
michael@0 | 966 | self.rankSep = delegateProperty(position.rankSep); |
michael@0 | 967 | self.rankDir = util.propertyAccessor(self, config, 'rankDir'); |
michael@0 | 968 | self.debugAlignment = delegateProperty(position.debugAlignment); |
michael@0 | 969 | |
michael@0 | 970 | self.debugLevel = util.propertyAccessor(self, config, 'debugLevel', function(x) { |
michael@0 | 971 | util.log.level = x; |
michael@0 | 972 | position.debugLevel(x); |
michael@0 | 973 | }); |
michael@0 | 974 | |
michael@0 | 975 | self.run = util.time('Total layout', run); |
michael@0 | 976 | |
michael@0 | 977 | self._normalize = normalize; |
michael@0 | 978 | |
michael@0 | 979 | return self; |
michael@0 | 980 | |
michael@0 | 981 | /* |
michael@0 | 982 | * Constructs an adjacency graph using the nodes and edges specified through |
michael@0 | 983 | * config. For each node and edge we add a property `dagre` that contains an |
michael@0 | 984 | * object that will hold intermediate and final layout information. Some of |
michael@0 | 985 | * the contents include: |
michael@0 | 986 | * |
michael@0 | 987 | * 1) A generated ID that uniquely identifies the object. |
michael@0 | 988 | * 2) Dimension information for nodes (copied from the source node). |
michael@0 | 989 | * 3) Optional dimension information for edges. |
michael@0 | 990 | * |
michael@0 | 991 | * After the adjacency graph is constructed the code no longer needs to use |
michael@0 | 992 | * the original nodes and edges passed in via config. |
michael@0 | 993 | */ |
michael@0 | 994 | function initLayoutGraph(inputGraph) { |
michael@0 | 995 | var g = new CDigraph(); |
michael@0 | 996 | |
michael@0 | 997 | inputGraph.eachNode(function(u, value) { |
michael@0 | 998 | if (value === undefined) value = {}; |
michael@0 | 999 | g.addNode(u, { |
michael@0 | 1000 | width: value.width, |
michael@0 | 1001 | height: value.height |
michael@0 | 1002 | }); |
michael@0 | 1003 | if (value.hasOwnProperty('rank')) { |
michael@0 | 1004 | g.node(u).prefRank = value.rank; |
michael@0 | 1005 | } |
michael@0 | 1006 | }); |
michael@0 | 1007 | |
michael@0 | 1008 | // Set up subgraphs |
michael@0 | 1009 | if (inputGraph.parent) { |
michael@0 | 1010 | inputGraph.nodes().forEach(function(u) { |
michael@0 | 1011 | g.parent(u, inputGraph.parent(u)); |
michael@0 | 1012 | }); |
michael@0 | 1013 | } |
michael@0 | 1014 | |
michael@0 | 1015 | inputGraph.eachEdge(function(e, u, v, value) { |
michael@0 | 1016 | if (value === undefined) value = {}; |
michael@0 | 1017 | var newValue = { |
michael@0 | 1018 | e: e, |
michael@0 | 1019 | minLen: value.minLen || 1, |
michael@0 | 1020 | width: value.width || 0, |
michael@0 | 1021 | height: value.height || 0, |
michael@0 | 1022 | points: [] |
michael@0 | 1023 | }; |
michael@0 | 1024 | |
michael@0 | 1025 | g.addEdge(null, u, v, newValue); |
michael@0 | 1026 | }); |
michael@0 | 1027 | |
michael@0 | 1028 | // Initial graph attributes |
michael@0 | 1029 | var graphValue = inputGraph.graph() || {}; |
michael@0 | 1030 | g.graph({ |
michael@0 | 1031 | rankDir: graphValue.rankDir || config.rankDir, |
michael@0 | 1032 | orderRestarts: graphValue.orderRestarts |
michael@0 | 1033 | }); |
michael@0 | 1034 | |
michael@0 | 1035 | return g; |
michael@0 | 1036 | } |
michael@0 | 1037 | |
michael@0 | 1038 | function run(inputGraph) { |
michael@0 | 1039 | var rankSep = self.rankSep(); |
michael@0 | 1040 | var g; |
michael@0 | 1041 | try { |
michael@0 | 1042 | // Build internal graph |
michael@0 | 1043 | g = util.time('initLayoutGraph', initLayoutGraph)(inputGraph); |
michael@0 | 1044 | |
michael@0 | 1045 | if (g.order() === 0) { |
michael@0 | 1046 | return g; |
michael@0 | 1047 | } |
michael@0 | 1048 | |
michael@0 | 1049 | // Make space for edge labels |
michael@0 | 1050 | g.eachEdge(function(e, s, t, a) { |
michael@0 | 1051 | a.minLen *= 2; |
michael@0 | 1052 | }); |
michael@0 | 1053 | self.rankSep(rankSep / 2); |
michael@0 | 1054 | |
michael@0 | 1055 | // Determine the rank for each node. Nodes with a lower rank will appear |
michael@0 | 1056 | // above nodes of higher rank. |
michael@0 | 1057 | util.time('rank.run', rank.run)(g, config.rankSimplex); |
michael@0 | 1058 | |
michael@0 | 1059 | // Normalize the graph by ensuring that every edge is proper (each edge has |
michael@0 | 1060 | // a length of 1). We achieve this by adding dummy nodes to long edges, |
michael@0 | 1061 | // thus shortening them. |
michael@0 | 1062 | util.time('normalize', normalize)(g); |
michael@0 | 1063 | |
michael@0 | 1064 | // Order the nodes so that edge crossings are minimized. |
michael@0 | 1065 | util.time('order', order)(g, config.orderMaxSweeps); |
michael@0 | 1066 | |
michael@0 | 1067 | // Find the x and y coordinates for every node in the graph. |
michael@0 | 1068 | util.time('position', position.run)(g); |
michael@0 | 1069 | |
michael@0 | 1070 | // De-normalize the graph by removing dummy nodes and augmenting the |
michael@0 | 1071 | // original long edges with coordinate information. |
michael@0 | 1072 | util.time('undoNormalize', undoNormalize)(g); |
michael@0 | 1073 | |
michael@0 | 1074 | // Reverses points for edges that are in a reversed state. |
michael@0 | 1075 | util.time('fixupEdgePoints', fixupEdgePoints)(g); |
michael@0 | 1076 | |
michael@0 | 1077 | // Restore delete edges and reverse edges that were reversed in the rank |
michael@0 | 1078 | // phase. |
michael@0 | 1079 | util.time('rank.restoreEdges', rank.restoreEdges)(g); |
michael@0 | 1080 | |
michael@0 | 1081 | // Construct final result graph and return it |
michael@0 | 1082 | return util.time('createFinalGraph', createFinalGraph)(g, inputGraph.isDirected()); |
michael@0 | 1083 | } finally { |
michael@0 | 1084 | self.rankSep(rankSep); |
michael@0 | 1085 | } |
michael@0 | 1086 | } |
michael@0 | 1087 | |
michael@0 | 1088 | /* |
michael@0 | 1089 | * This function is responsible for 'normalizing' the graph. The process of |
michael@0 | 1090 | * normalization ensures that no edge in the graph has spans more than one |
michael@0 | 1091 | * rank. To do this it inserts dummy nodes as needed and links them by adding |
michael@0 | 1092 | * dummy edges. This function keeps enough information in the dummy nodes and |
michael@0 | 1093 | * edges to ensure that the original graph can be reconstructed later. |
michael@0 | 1094 | * |
michael@0 | 1095 | * This method assumes that the input graph is cycle free. |
michael@0 | 1096 | */ |
michael@0 | 1097 | function normalize(g) { |
michael@0 | 1098 | var dummyCount = 0; |
michael@0 | 1099 | g.eachEdge(function(e, s, t, a) { |
michael@0 | 1100 | var sourceRank = g.node(s).rank; |
michael@0 | 1101 | var targetRank = g.node(t).rank; |
michael@0 | 1102 | if (sourceRank + 1 < targetRank) { |
michael@0 | 1103 | for (var u = s, rank = sourceRank + 1, i = 0; rank < targetRank; ++rank, ++i) { |
michael@0 | 1104 | var v = '_D' + (++dummyCount); |
michael@0 | 1105 | var node = { |
michael@0 | 1106 | width: a.width, |
michael@0 | 1107 | height: a.height, |
michael@0 | 1108 | edge: { id: e, source: s, target: t, attrs: a }, |
michael@0 | 1109 | rank: rank, |
michael@0 | 1110 | dummy: true |
michael@0 | 1111 | }; |
michael@0 | 1112 | |
michael@0 | 1113 | // If this node represents a bend then we will use it as a control |
michael@0 | 1114 | // point. For edges with 2 segments this will be the center dummy |
michael@0 | 1115 | // node. For edges with more than two segments, this will be the |
michael@0 | 1116 | // first and last dummy node. |
michael@0 | 1117 | if (i === 0) node.index = 0; |
michael@0 | 1118 | else if (rank + 1 === targetRank) node.index = 1; |
michael@0 | 1119 | |
michael@0 | 1120 | g.addNode(v, node); |
michael@0 | 1121 | g.addEdge(null, u, v, {}); |
michael@0 | 1122 | u = v; |
michael@0 | 1123 | } |
michael@0 | 1124 | g.addEdge(null, u, t, {}); |
michael@0 | 1125 | g.delEdge(e); |
michael@0 | 1126 | } |
michael@0 | 1127 | }); |
michael@0 | 1128 | } |
michael@0 | 1129 | |
michael@0 | 1130 | /* |
michael@0 | 1131 | * Reconstructs the graph as it was before normalization. The positions of |
michael@0 | 1132 | * dummy nodes are used to build an array of points for the original 'long' |
michael@0 | 1133 | * edge. Dummy nodes and edges are removed. |
michael@0 | 1134 | */ |
michael@0 | 1135 | function undoNormalize(g) { |
michael@0 | 1136 | g.eachNode(function(u, a) { |
michael@0 | 1137 | if (a.dummy) { |
michael@0 | 1138 | if ('index' in a) { |
michael@0 | 1139 | var edge = a.edge; |
michael@0 | 1140 | if (!g.hasEdge(edge.id)) { |
michael@0 | 1141 | g.addEdge(edge.id, edge.source, edge.target, edge.attrs); |
michael@0 | 1142 | } |
michael@0 | 1143 | var points = g.edge(edge.id).points; |
michael@0 | 1144 | points[a.index] = { x: a.x, y: a.y, ul: a.ul, ur: a.ur, dl: a.dl, dr: a.dr }; |
michael@0 | 1145 | } |
michael@0 | 1146 | g.delNode(u); |
michael@0 | 1147 | } |
michael@0 | 1148 | }); |
michael@0 | 1149 | } |
michael@0 | 1150 | |
michael@0 | 1151 | /* |
michael@0 | 1152 | * For each edge that was reversed during the `acyclic` step, reverse its |
michael@0 | 1153 | * array of points. |
michael@0 | 1154 | */ |
michael@0 | 1155 | function fixupEdgePoints(g) { |
michael@0 | 1156 | g.eachEdge(function(e, s, t, a) { if (a.reversed) a.points.reverse(); }); |
michael@0 | 1157 | } |
michael@0 | 1158 | |
michael@0 | 1159 | function createFinalGraph(g, isDirected) { |
michael@0 | 1160 | var out = isDirected ? new CDigraph() : new CGraph(); |
michael@0 | 1161 | out.graph(g.graph()); |
michael@0 | 1162 | g.eachNode(function(u, value) { out.addNode(u, value); }); |
michael@0 | 1163 | g.eachNode(function(u) { out.parent(u, g.parent(u)); }); |
michael@0 | 1164 | g.eachEdge(function(e, u, v, value) { |
michael@0 | 1165 | out.addEdge(value.e, u, v, value); |
michael@0 | 1166 | }); |
michael@0 | 1167 | |
michael@0 | 1168 | // Attach bounding box information |
michael@0 | 1169 | var maxX = 0, maxY = 0; |
michael@0 | 1170 | g.eachNode(function(u, value) { |
michael@0 | 1171 | if (!g.children(u).length) { |
michael@0 | 1172 | maxX = Math.max(maxX, value.x + value.width / 2); |
michael@0 | 1173 | maxY = Math.max(maxY, value.y + value.height / 2); |
michael@0 | 1174 | } |
michael@0 | 1175 | }); |
michael@0 | 1176 | g.eachEdge(function(e, u, v, value) { |
michael@0 | 1177 | var maxXPoints = Math.max.apply(Math, value.points.map(function(p) { return p.x; })); |
michael@0 | 1178 | var maxYPoints = Math.max.apply(Math, value.points.map(function(p) { return p.y; })); |
michael@0 | 1179 | maxX = Math.max(maxX, maxXPoints + value.width / 2); |
michael@0 | 1180 | maxY = Math.max(maxY, maxYPoints + value.height / 2); |
michael@0 | 1181 | }); |
michael@0 | 1182 | out.graph().width = maxX; |
michael@0 | 1183 | out.graph().height = maxY; |
michael@0 | 1184 | |
michael@0 | 1185 | return out; |
michael@0 | 1186 | } |
michael@0 | 1187 | |
michael@0 | 1188 | /* |
michael@0 | 1189 | * Given a function, a new function is returned that invokes the given |
michael@0 | 1190 | * function. The return value from the function is always the `self` object. |
michael@0 | 1191 | */ |
michael@0 | 1192 | function delegateProperty(f) { |
michael@0 | 1193 | return function() { |
michael@0 | 1194 | if (!arguments.length) return f(); |
michael@0 | 1195 | f.apply(null, arguments); |
michael@0 | 1196 | return self; |
michael@0 | 1197 | }; |
michael@0 | 1198 | } |
michael@0 | 1199 | }; |
michael@0 | 1200 | |
michael@0 | 1201 | |
michael@0 | 1202 | },{"./order":13,"./position":18,"./rank":19,"./util":26,"graphlib":28}],13:[function(require,module,exports){ |
michael@0 | 1203 | var util = require('./util'), |
michael@0 | 1204 | crossCount = require('./order/crossCount'), |
michael@0 | 1205 | initLayerGraphs = require('./order/initLayerGraphs'), |
michael@0 | 1206 | initOrder = require('./order/initOrder'), |
michael@0 | 1207 | sortLayer = require('./order/sortLayer'); |
michael@0 | 1208 | |
michael@0 | 1209 | module.exports = order; |
michael@0 | 1210 | |
michael@0 | 1211 | // The maximum number of sweeps to perform before finishing the order phase. |
michael@0 | 1212 | var DEFAULT_MAX_SWEEPS = 24; |
michael@0 | 1213 | order.DEFAULT_MAX_SWEEPS = DEFAULT_MAX_SWEEPS; |
michael@0 | 1214 | |
michael@0 | 1215 | /* |
michael@0 | 1216 | * Runs the order phase with the specified `graph, `maxSweeps`, and |
michael@0 | 1217 | * `debugLevel`. If `maxSweeps` is not specified we use `DEFAULT_MAX_SWEEPS`. |
michael@0 | 1218 | * If `debugLevel` is not set we assume 0. |
michael@0 | 1219 | */ |
michael@0 | 1220 | function order(g, maxSweeps) { |
michael@0 | 1221 | if (arguments.length < 2) { |
michael@0 | 1222 | maxSweeps = DEFAULT_MAX_SWEEPS; |
michael@0 | 1223 | } |
michael@0 | 1224 | |
michael@0 | 1225 | var restarts = g.graph().orderRestarts || 0; |
michael@0 | 1226 | |
michael@0 | 1227 | var layerGraphs = initLayerGraphs(g); |
michael@0 | 1228 | // TODO: remove this when we add back support for ordering clusters |
michael@0 | 1229 | layerGraphs.forEach(function(lg) { |
michael@0 | 1230 | lg = lg.filterNodes(function(u) { return !g.children(u).length; }); |
michael@0 | 1231 | }); |
michael@0 | 1232 | |
michael@0 | 1233 | var iters = 0, |
michael@0 | 1234 | currentBestCC, |
michael@0 | 1235 | allTimeBestCC = Number.MAX_VALUE, |
michael@0 | 1236 | allTimeBest = {}; |
michael@0 | 1237 | |
michael@0 | 1238 | function saveAllTimeBest() { |
michael@0 | 1239 | g.eachNode(function(u, value) { allTimeBest[u] = value.order; }); |
michael@0 | 1240 | } |
michael@0 | 1241 | |
michael@0 | 1242 | for (var j = 0; j < Number(restarts) + 1 && allTimeBestCC !== 0; ++j) { |
michael@0 | 1243 | currentBestCC = Number.MAX_VALUE; |
michael@0 | 1244 | initOrder(g, restarts > 0); |
michael@0 | 1245 | |
michael@0 | 1246 | util.log(2, 'Order phase start cross count: ' + g.graph().orderInitCC); |
michael@0 | 1247 | |
michael@0 | 1248 | var i, lastBest, cc; |
michael@0 | 1249 | for (i = 0, lastBest = 0; lastBest < 4 && i < maxSweeps && currentBestCC > 0; ++i, ++lastBest, ++iters) { |
michael@0 | 1250 | sweep(g, layerGraphs, i); |
michael@0 | 1251 | cc = crossCount(g); |
michael@0 | 1252 | if (cc < currentBestCC) { |
michael@0 | 1253 | lastBest = 0; |
michael@0 | 1254 | currentBestCC = cc; |
michael@0 | 1255 | if (cc < allTimeBestCC) { |
michael@0 | 1256 | saveAllTimeBest(); |
michael@0 | 1257 | allTimeBestCC = cc; |
michael@0 | 1258 | } |
michael@0 | 1259 | } |
michael@0 | 1260 | util.log(3, 'Order phase start ' + j + ' iter ' + i + ' cross count: ' + cc); |
michael@0 | 1261 | } |
michael@0 | 1262 | } |
michael@0 | 1263 | |
michael@0 | 1264 | Object.keys(allTimeBest).forEach(function(u) { |
michael@0 | 1265 | if (!g.children || !g.children(u).length) { |
michael@0 | 1266 | g.node(u).order = allTimeBest[u]; |
michael@0 | 1267 | } |
michael@0 | 1268 | }); |
michael@0 | 1269 | g.graph().orderCC = allTimeBestCC; |
michael@0 | 1270 | |
michael@0 | 1271 | util.log(2, 'Order iterations: ' + iters); |
michael@0 | 1272 | util.log(2, 'Order phase best cross count: ' + g.graph().orderCC); |
michael@0 | 1273 | } |
michael@0 | 1274 | |
michael@0 | 1275 | function predecessorWeights(g, nodes) { |
michael@0 | 1276 | var weights = {}; |
michael@0 | 1277 | nodes.forEach(function(u) { |
michael@0 | 1278 | weights[u] = g.inEdges(u).map(function(e) { |
michael@0 | 1279 | return g.node(g.source(e)).order; |
michael@0 | 1280 | }); |
michael@0 | 1281 | }); |
michael@0 | 1282 | return weights; |
michael@0 | 1283 | } |
michael@0 | 1284 | |
michael@0 | 1285 | function successorWeights(g, nodes) { |
michael@0 | 1286 | var weights = {}; |
michael@0 | 1287 | nodes.forEach(function(u) { |
michael@0 | 1288 | weights[u] = g.outEdges(u).map(function(e) { |
michael@0 | 1289 | return g.node(g.target(e)).order; |
michael@0 | 1290 | }); |
michael@0 | 1291 | }); |
michael@0 | 1292 | return weights; |
michael@0 | 1293 | } |
michael@0 | 1294 | |
michael@0 | 1295 | function sweep(g, layerGraphs, iter) { |
michael@0 | 1296 | if (iter % 2 === 0) { |
michael@0 | 1297 | sweepDown(g, layerGraphs, iter); |
michael@0 | 1298 | } else { |
michael@0 | 1299 | sweepUp(g, layerGraphs, iter); |
michael@0 | 1300 | } |
michael@0 | 1301 | } |
michael@0 | 1302 | |
michael@0 | 1303 | function sweepDown(g, layerGraphs) { |
michael@0 | 1304 | var cg; |
michael@0 | 1305 | for (i = 1; i < layerGraphs.length; ++i) { |
michael@0 | 1306 | cg = sortLayer(layerGraphs[i], cg, predecessorWeights(g, layerGraphs[i].nodes())); |
michael@0 | 1307 | } |
michael@0 | 1308 | } |
michael@0 | 1309 | |
michael@0 | 1310 | function sweepUp(g, layerGraphs) { |
michael@0 | 1311 | var cg; |
michael@0 | 1312 | for (i = layerGraphs.length - 2; i >= 0; --i) { |
michael@0 | 1313 | sortLayer(layerGraphs[i], cg, successorWeights(g, layerGraphs[i].nodes())); |
michael@0 | 1314 | } |
michael@0 | 1315 | } |
michael@0 | 1316 | |
michael@0 | 1317 | },{"./order/crossCount":14,"./order/initLayerGraphs":15,"./order/initOrder":16,"./order/sortLayer":17,"./util":26}],14:[function(require,module,exports){ |
michael@0 | 1318 | var util = require('../util'); |
michael@0 | 1319 | |
michael@0 | 1320 | module.exports = crossCount; |
michael@0 | 1321 | |
michael@0 | 1322 | /* |
michael@0 | 1323 | * Returns the cross count for the given graph. |
michael@0 | 1324 | */ |
michael@0 | 1325 | function crossCount(g) { |
michael@0 | 1326 | var cc = 0; |
michael@0 | 1327 | var ordering = util.ordering(g); |
michael@0 | 1328 | for (var i = 1; i < ordering.length; ++i) { |
michael@0 | 1329 | cc += twoLayerCrossCount(g, ordering[i-1], ordering[i]); |
michael@0 | 1330 | } |
michael@0 | 1331 | return cc; |
michael@0 | 1332 | } |
michael@0 | 1333 | |
michael@0 | 1334 | /* |
michael@0 | 1335 | * This function searches through a ranked and ordered graph and counts the |
michael@0 | 1336 | * number of edges that cross. This algorithm is derived from: |
michael@0 | 1337 | * |
michael@0 | 1338 | * W. Barth et al., Bilayer Cross Counting, JGAA, 8(2) 179–194 (2004) |
michael@0 | 1339 | */ |
michael@0 | 1340 | function twoLayerCrossCount(g, layer1, layer2) { |
michael@0 | 1341 | var indices = []; |
michael@0 | 1342 | layer1.forEach(function(u) { |
michael@0 | 1343 | var nodeIndices = []; |
michael@0 | 1344 | g.outEdges(u).forEach(function(e) { nodeIndices.push(g.node(g.target(e)).order); }); |
michael@0 | 1345 | nodeIndices.sort(function(x, y) { return x - y; }); |
michael@0 | 1346 | indices = indices.concat(nodeIndices); |
michael@0 | 1347 | }); |
michael@0 | 1348 | |
michael@0 | 1349 | var firstIndex = 1; |
michael@0 | 1350 | while (firstIndex < layer2.length) firstIndex <<= 1; |
michael@0 | 1351 | |
michael@0 | 1352 | var treeSize = 2 * firstIndex - 1; |
michael@0 | 1353 | firstIndex -= 1; |
michael@0 | 1354 | |
michael@0 | 1355 | var tree = []; |
michael@0 | 1356 | for (var i = 0; i < treeSize; ++i) { tree[i] = 0; } |
michael@0 | 1357 | |
michael@0 | 1358 | var cc = 0; |
michael@0 | 1359 | indices.forEach(function(i) { |
michael@0 | 1360 | var treeIndex = i + firstIndex; |
michael@0 | 1361 | ++tree[treeIndex]; |
michael@0 | 1362 | while (treeIndex > 0) { |
michael@0 | 1363 | if (treeIndex % 2) { |
michael@0 | 1364 | cc += tree[treeIndex + 1]; |
michael@0 | 1365 | } |
michael@0 | 1366 | treeIndex = (treeIndex - 1) >> 1; |
michael@0 | 1367 | ++tree[treeIndex]; |
michael@0 | 1368 | } |
michael@0 | 1369 | }); |
michael@0 | 1370 | |
michael@0 | 1371 | return cc; |
michael@0 | 1372 | } |
michael@0 | 1373 | |
michael@0 | 1374 | },{"../util":26}],15:[function(require,module,exports){ |
michael@0 | 1375 | var nodesFromList = require('graphlib').filter.nodesFromList, |
michael@0 | 1376 | /* jshint -W079 */ |
michael@0 | 1377 | Set = require('cp-data').Set; |
michael@0 | 1378 | |
michael@0 | 1379 | module.exports = initLayerGraphs; |
michael@0 | 1380 | |
michael@0 | 1381 | /* |
michael@0 | 1382 | * This function takes a compound layered graph, g, and produces an array of |
michael@0 | 1383 | * layer graphs. Each entry in the array represents a subgraph of nodes |
michael@0 | 1384 | * relevant for performing crossing reduction on that layer. |
michael@0 | 1385 | */ |
michael@0 | 1386 | function initLayerGraphs(g) { |
michael@0 | 1387 | var ranks = []; |
michael@0 | 1388 | |
michael@0 | 1389 | function dfs(u) { |
michael@0 | 1390 | if (u === null) { |
michael@0 | 1391 | g.children(u).forEach(function(v) { dfs(v); }); |
michael@0 | 1392 | return; |
michael@0 | 1393 | } |
michael@0 | 1394 | |
michael@0 | 1395 | var value = g.node(u); |
michael@0 | 1396 | value.minRank = ('rank' in value) ? value.rank : Number.MAX_VALUE; |
michael@0 | 1397 | value.maxRank = ('rank' in value) ? value.rank : Number.MIN_VALUE; |
michael@0 | 1398 | var uRanks = new Set(); |
michael@0 | 1399 | g.children(u).forEach(function(v) { |
michael@0 | 1400 | var rs = dfs(v); |
michael@0 | 1401 | uRanks = Set.union([uRanks, rs]); |
michael@0 | 1402 | value.minRank = Math.min(value.minRank, g.node(v).minRank); |
michael@0 | 1403 | value.maxRank = Math.max(value.maxRank, g.node(v).maxRank); |
michael@0 | 1404 | }); |
michael@0 | 1405 | |
michael@0 | 1406 | if ('rank' in value) uRanks.add(value.rank); |
michael@0 | 1407 | |
michael@0 | 1408 | uRanks.keys().forEach(function(r) { |
michael@0 | 1409 | if (!(r in ranks)) ranks[r] = []; |
michael@0 | 1410 | ranks[r].push(u); |
michael@0 | 1411 | }); |
michael@0 | 1412 | |
michael@0 | 1413 | return uRanks; |
michael@0 | 1414 | } |
michael@0 | 1415 | dfs(null); |
michael@0 | 1416 | |
michael@0 | 1417 | var layerGraphs = []; |
michael@0 | 1418 | ranks.forEach(function(us, rank) { |
michael@0 | 1419 | layerGraphs[rank] = g.filterNodes(nodesFromList(us)); |
michael@0 | 1420 | }); |
michael@0 | 1421 | |
michael@0 | 1422 | return layerGraphs; |
michael@0 | 1423 | } |
michael@0 | 1424 | |
michael@0 | 1425 | },{"cp-data":5,"graphlib":28}],16:[function(require,module,exports){ |
michael@0 | 1426 | var crossCount = require('./crossCount'), |
michael@0 | 1427 | util = require('../util'); |
michael@0 | 1428 | |
michael@0 | 1429 | module.exports = initOrder; |
michael@0 | 1430 | |
michael@0 | 1431 | /* |
michael@0 | 1432 | * Given a graph with a set of layered nodes (i.e. nodes that have a `rank` |
michael@0 | 1433 | * attribute) this function attaches an `order` attribute that uniquely |
michael@0 | 1434 | * arranges each node of each rank. If no constraint graph is provided the |
michael@0 | 1435 | * order of the nodes in each rank is entirely arbitrary. |
michael@0 | 1436 | */ |
michael@0 | 1437 | function initOrder(g, random) { |
michael@0 | 1438 | var layers = []; |
michael@0 | 1439 | |
michael@0 | 1440 | g.eachNode(function(u, value) { |
michael@0 | 1441 | var layer = layers[value.rank]; |
michael@0 | 1442 | if (g.children && g.children(u).length > 0) return; |
michael@0 | 1443 | if (!layer) { |
michael@0 | 1444 | layer = layers[value.rank] = []; |
michael@0 | 1445 | } |
michael@0 | 1446 | layer.push(u); |
michael@0 | 1447 | }); |
michael@0 | 1448 | |
michael@0 | 1449 | layers.forEach(function(layer) { |
michael@0 | 1450 | if (random) { |
michael@0 | 1451 | util.shuffle(layer); |
michael@0 | 1452 | } |
michael@0 | 1453 | layer.forEach(function(u, i) { |
michael@0 | 1454 | g.node(u).order = i; |
michael@0 | 1455 | }); |
michael@0 | 1456 | }); |
michael@0 | 1457 | |
michael@0 | 1458 | var cc = crossCount(g); |
michael@0 | 1459 | g.graph().orderInitCC = cc; |
michael@0 | 1460 | g.graph().orderCC = Number.MAX_VALUE; |
michael@0 | 1461 | } |
michael@0 | 1462 | |
michael@0 | 1463 | },{"../util":26,"./crossCount":14}],17:[function(require,module,exports){ |
michael@0 | 1464 | var util = require('../util'); |
michael@0 | 1465 | /* |
michael@0 | 1466 | Digraph = require('graphlib').Digraph, |
michael@0 | 1467 | topsort = require('graphlib').alg.topsort, |
michael@0 | 1468 | nodesFromList = require('graphlib').filter.nodesFromList; |
michael@0 | 1469 | */ |
michael@0 | 1470 | |
michael@0 | 1471 | module.exports = sortLayer; |
michael@0 | 1472 | |
michael@0 | 1473 | /* |
michael@0 | 1474 | function sortLayer(g, cg, weights) { |
michael@0 | 1475 | var result = sortLayerSubgraph(g, null, cg, weights); |
michael@0 | 1476 | result.list.forEach(function(u, i) { |
michael@0 | 1477 | g.node(u).order = i; |
michael@0 | 1478 | }); |
michael@0 | 1479 | return result.constraintGraph; |
michael@0 | 1480 | } |
michael@0 | 1481 | */ |
michael@0 | 1482 | |
michael@0 | 1483 | function sortLayer(g, cg, weights) { |
michael@0 | 1484 | var ordering = []; |
michael@0 | 1485 | var bs = {}; |
michael@0 | 1486 | g.eachNode(function(u, value) { |
michael@0 | 1487 | ordering[value.order] = u; |
michael@0 | 1488 | var ws = weights[u]; |
michael@0 | 1489 | if (ws.length) { |
michael@0 | 1490 | bs[u] = util.sum(ws) / ws.length; |
michael@0 | 1491 | } |
michael@0 | 1492 | }); |
michael@0 | 1493 | |
michael@0 | 1494 | var toSort = g.nodes().filter(function(u) { return bs[u] !== undefined; }); |
michael@0 | 1495 | toSort.sort(function(x, y) { |
michael@0 | 1496 | return bs[x] - bs[y] || g.node(x).order - g.node(y).order; |
michael@0 | 1497 | }); |
michael@0 | 1498 | |
michael@0 | 1499 | for (var i = 0, j = 0, jl = toSort.length; j < jl; ++i) { |
michael@0 | 1500 | if (bs[ordering[i]] !== undefined) { |
michael@0 | 1501 | g.node(toSort[j++]).order = i; |
michael@0 | 1502 | } |
michael@0 | 1503 | } |
michael@0 | 1504 | } |
michael@0 | 1505 | |
michael@0 | 1506 | // TOOD: re-enable constrained sorting once we have a strategy for handling |
michael@0 | 1507 | // undefined barycenters. |
michael@0 | 1508 | /* |
michael@0 | 1509 | function sortLayerSubgraph(g, sg, cg, weights) { |
michael@0 | 1510 | cg = cg ? cg.filterNodes(nodesFromList(g.children(sg))) : new Digraph(); |
michael@0 | 1511 | |
michael@0 | 1512 | var nodeData = {}; |
michael@0 | 1513 | g.children(sg).forEach(function(u) { |
michael@0 | 1514 | if (g.children(u).length) { |
michael@0 | 1515 | nodeData[u] = sortLayerSubgraph(g, u, cg, weights); |
michael@0 | 1516 | nodeData[u].firstSG = u; |
michael@0 | 1517 | nodeData[u].lastSG = u; |
michael@0 | 1518 | } else { |
michael@0 | 1519 | var ws = weights[u]; |
michael@0 | 1520 | nodeData[u] = { |
michael@0 | 1521 | degree: ws.length, |
michael@0 | 1522 | barycenter: ws.length > 0 ? util.sum(ws) / ws.length : 0, |
michael@0 | 1523 | list: [u] |
michael@0 | 1524 | }; |
michael@0 | 1525 | } |
michael@0 | 1526 | }); |
michael@0 | 1527 | |
michael@0 | 1528 | resolveViolatedConstraints(g, cg, nodeData); |
michael@0 | 1529 | |
michael@0 | 1530 | var keys = Object.keys(nodeData); |
michael@0 | 1531 | keys.sort(function(x, y) { |
michael@0 | 1532 | return nodeData[x].barycenter - nodeData[y].barycenter; |
michael@0 | 1533 | }); |
michael@0 | 1534 | |
michael@0 | 1535 | var result = keys.map(function(u) { return nodeData[u]; }) |
michael@0 | 1536 | .reduce(function(lhs, rhs) { return mergeNodeData(g, lhs, rhs); }); |
michael@0 | 1537 | return result; |
michael@0 | 1538 | } |
michael@0 | 1539 | |
michael@0 | 1540 | /* |
michael@0 | 1541 | function mergeNodeData(g, lhs, rhs) { |
michael@0 | 1542 | var cg = mergeDigraphs(lhs.constraintGraph, rhs.constraintGraph); |
michael@0 | 1543 | |
michael@0 | 1544 | if (lhs.lastSG !== undefined && rhs.firstSG !== undefined) { |
michael@0 | 1545 | if (cg === undefined) { |
michael@0 | 1546 | cg = new Digraph(); |
michael@0 | 1547 | } |
michael@0 | 1548 | if (!cg.hasNode(lhs.lastSG)) { cg.addNode(lhs.lastSG); } |
michael@0 | 1549 | cg.addNode(rhs.firstSG); |
michael@0 | 1550 | cg.addEdge(null, lhs.lastSG, rhs.firstSG); |
michael@0 | 1551 | } |
michael@0 | 1552 | |
michael@0 | 1553 | return { |
michael@0 | 1554 | degree: lhs.degree + rhs.degree, |
michael@0 | 1555 | barycenter: (lhs.barycenter * lhs.degree + rhs.barycenter * rhs.degree) / |
michael@0 | 1556 | (lhs.degree + rhs.degree), |
michael@0 | 1557 | list: lhs.list.concat(rhs.list), |
michael@0 | 1558 | firstSG: lhs.firstSG !== undefined ? lhs.firstSG : rhs.firstSG, |
michael@0 | 1559 | lastSG: rhs.lastSG !== undefined ? rhs.lastSG : lhs.lastSG, |
michael@0 | 1560 | constraintGraph: cg |
michael@0 | 1561 | }; |
michael@0 | 1562 | } |
michael@0 | 1563 | |
michael@0 | 1564 | function mergeDigraphs(lhs, rhs) { |
michael@0 | 1565 | if (lhs === undefined) return rhs; |
michael@0 | 1566 | if (rhs === undefined) return lhs; |
michael@0 | 1567 | |
michael@0 | 1568 | lhs = lhs.copy(); |
michael@0 | 1569 | rhs.nodes().forEach(function(u) { lhs.addNode(u); }); |
michael@0 | 1570 | rhs.edges().forEach(function(e, u, v) { lhs.addEdge(null, u, v); }); |
michael@0 | 1571 | return lhs; |
michael@0 | 1572 | } |
michael@0 | 1573 | |
michael@0 | 1574 | function resolveViolatedConstraints(g, cg, nodeData) { |
michael@0 | 1575 | // Removes nodes `u` and `v` from `cg` and makes any edges incident on them |
michael@0 | 1576 | // incident on `w` instead. |
michael@0 | 1577 | function collapseNodes(u, v, w) { |
michael@0 | 1578 | // TODO original paper removes self loops, but it is not obvious when this would happen |
michael@0 | 1579 | cg.inEdges(u).forEach(function(e) { |
michael@0 | 1580 | cg.delEdge(e); |
michael@0 | 1581 | cg.addEdge(null, cg.source(e), w); |
michael@0 | 1582 | }); |
michael@0 | 1583 | |
michael@0 | 1584 | cg.outEdges(v).forEach(function(e) { |
michael@0 | 1585 | cg.delEdge(e); |
michael@0 | 1586 | cg.addEdge(null, w, cg.target(e)); |
michael@0 | 1587 | }); |
michael@0 | 1588 | |
michael@0 | 1589 | cg.delNode(u); |
michael@0 | 1590 | cg.delNode(v); |
michael@0 | 1591 | } |
michael@0 | 1592 | |
michael@0 | 1593 | var violated; |
michael@0 | 1594 | while ((violated = findViolatedConstraint(cg, nodeData)) !== undefined) { |
michael@0 | 1595 | var source = cg.source(violated), |
michael@0 | 1596 | target = cg.target(violated); |
michael@0 | 1597 | |
michael@0 | 1598 | var v; |
michael@0 | 1599 | while ((v = cg.addNode(null)) && g.hasNode(v)) { |
michael@0 | 1600 | cg.delNode(v); |
michael@0 | 1601 | } |
michael@0 | 1602 | |
michael@0 | 1603 | // Collapse barycenter and list |
michael@0 | 1604 | nodeData[v] = mergeNodeData(g, nodeData[source], nodeData[target]); |
michael@0 | 1605 | delete nodeData[source]; |
michael@0 | 1606 | delete nodeData[target]; |
michael@0 | 1607 | |
michael@0 | 1608 | collapseNodes(source, target, v); |
michael@0 | 1609 | if (cg.incidentEdges(v).length === 0) { cg.delNode(v); } |
michael@0 | 1610 | } |
michael@0 | 1611 | } |
michael@0 | 1612 | |
michael@0 | 1613 | function findViolatedConstraint(cg, nodeData) { |
michael@0 | 1614 | var us = topsort(cg); |
michael@0 | 1615 | for (var i = 0; i < us.length; ++i) { |
michael@0 | 1616 | var u = us[i]; |
michael@0 | 1617 | var inEdges = cg.inEdges(u); |
michael@0 | 1618 | for (var j = 0; j < inEdges.length; ++j) { |
michael@0 | 1619 | var e = inEdges[j]; |
michael@0 | 1620 | if (nodeData[cg.source(e)].barycenter >= nodeData[u].barycenter) { |
michael@0 | 1621 | return e; |
michael@0 | 1622 | } |
michael@0 | 1623 | } |
michael@0 | 1624 | } |
michael@0 | 1625 | } |
michael@0 | 1626 | */ |
michael@0 | 1627 | |
michael@0 | 1628 | },{"../util":26}],18:[function(require,module,exports){ |
michael@0 | 1629 | var util = require('./util'); |
michael@0 | 1630 | |
michael@0 | 1631 | /* |
michael@0 | 1632 | * The algorithms here are based on Brandes and Köpf, "Fast and Simple |
michael@0 | 1633 | * Horizontal Coordinate Assignment". |
michael@0 | 1634 | */ |
michael@0 | 1635 | module.exports = function() { |
michael@0 | 1636 | // External configuration |
michael@0 | 1637 | var config = { |
michael@0 | 1638 | nodeSep: 50, |
michael@0 | 1639 | edgeSep: 10, |
michael@0 | 1640 | universalSep: null, |
michael@0 | 1641 | rankSep: 30 |
michael@0 | 1642 | }; |
michael@0 | 1643 | |
michael@0 | 1644 | var self = {}; |
michael@0 | 1645 | |
michael@0 | 1646 | self.nodeSep = util.propertyAccessor(self, config, 'nodeSep'); |
michael@0 | 1647 | self.edgeSep = util.propertyAccessor(self, config, 'edgeSep'); |
michael@0 | 1648 | // If not null this separation value is used for all nodes and edges |
michael@0 | 1649 | // regardless of their widths. `nodeSep` and `edgeSep` are ignored with this |
michael@0 | 1650 | // option. |
michael@0 | 1651 | self.universalSep = util.propertyAccessor(self, config, 'universalSep'); |
michael@0 | 1652 | self.rankSep = util.propertyAccessor(self, config, 'rankSep'); |
michael@0 | 1653 | self.debugLevel = util.propertyAccessor(self, config, 'debugLevel'); |
michael@0 | 1654 | |
michael@0 | 1655 | self.run = run; |
michael@0 | 1656 | |
michael@0 | 1657 | return self; |
michael@0 | 1658 | |
michael@0 | 1659 | function run(g) { |
michael@0 | 1660 | g = g.filterNodes(util.filterNonSubgraphs(g)); |
michael@0 | 1661 | |
michael@0 | 1662 | var layering = util.ordering(g); |
michael@0 | 1663 | |
michael@0 | 1664 | var conflicts = findConflicts(g, layering); |
michael@0 | 1665 | |
michael@0 | 1666 | var xss = {}; |
michael@0 | 1667 | ['u', 'd'].forEach(function(vertDir) { |
michael@0 | 1668 | if (vertDir === 'd') layering.reverse(); |
michael@0 | 1669 | |
michael@0 | 1670 | ['l', 'r'].forEach(function(horizDir) { |
michael@0 | 1671 | if (horizDir === 'r') reverseInnerOrder(layering); |
michael@0 | 1672 | |
michael@0 | 1673 | var dir = vertDir + horizDir; |
michael@0 | 1674 | var align = verticalAlignment(g, layering, conflicts, vertDir === 'u' ? 'predecessors' : 'successors'); |
michael@0 | 1675 | xss[dir]= horizontalCompaction(g, layering, align.pos, align.root, align.align); |
michael@0 | 1676 | |
michael@0 | 1677 | if (config.debugLevel >= 3) |
michael@0 | 1678 | debugPositioning(vertDir + horizDir, g, layering, xss[dir]); |
michael@0 | 1679 | |
michael@0 | 1680 | if (horizDir === 'r') flipHorizontally(xss[dir]); |
michael@0 | 1681 | |
michael@0 | 1682 | if (horizDir === 'r') reverseInnerOrder(layering); |
michael@0 | 1683 | }); |
michael@0 | 1684 | |
michael@0 | 1685 | if (vertDir === 'd') layering.reverse(); |
michael@0 | 1686 | }); |
michael@0 | 1687 | |
michael@0 | 1688 | balance(g, layering, xss); |
michael@0 | 1689 | |
michael@0 | 1690 | g.eachNode(function(v) { |
michael@0 | 1691 | var xs = []; |
michael@0 | 1692 | for (var alignment in xss) { |
michael@0 | 1693 | var alignmentX = xss[alignment][v]; |
michael@0 | 1694 | posXDebug(alignment, g, v, alignmentX); |
michael@0 | 1695 | xs.push(alignmentX); |
michael@0 | 1696 | } |
michael@0 | 1697 | xs.sort(function(x, y) { return x - y; }); |
michael@0 | 1698 | posX(g, v, (xs[1] + xs[2]) / 2); |
michael@0 | 1699 | }); |
michael@0 | 1700 | |
michael@0 | 1701 | // Align y coordinates with ranks |
michael@0 | 1702 | var y = 0, reverseY = g.graph().rankDir === 'BT' || g.graph().rankDir === 'RL'; |
michael@0 | 1703 | layering.forEach(function(layer) { |
michael@0 | 1704 | var maxHeight = util.max(layer.map(function(u) { return height(g, u); })); |
michael@0 | 1705 | y += maxHeight / 2; |
michael@0 | 1706 | layer.forEach(function(u) { |
michael@0 | 1707 | posY(g, u, reverseY ? -y : y); |
michael@0 | 1708 | }); |
michael@0 | 1709 | y += maxHeight / 2 + config.rankSep; |
michael@0 | 1710 | }); |
michael@0 | 1711 | |
michael@0 | 1712 | // Translate layout so that top left corner of bounding rectangle has |
michael@0 | 1713 | // coordinate (0, 0). |
michael@0 | 1714 | var minX = util.min(g.nodes().map(function(u) { return posX(g, u) - width(g, u) / 2; })); |
michael@0 | 1715 | var minY = util.min(g.nodes().map(function(u) { return posY(g, u) - height(g, u) / 2; })); |
michael@0 | 1716 | g.eachNode(function(u) { |
michael@0 | 1717 | posX(g, u, posX(g, u) - minX); |
michael@0 | 1718 | posY(g, u, posY(g, u) - minY); |
michael@0 | 1719 | }); |
michael@0 | 1720 | } |
michael@0 | 1721 | |
michael@0 | 1722 | /* |
michael@0 | 1723 | * Generate an ID that can be used to represent any undirected edge that is |
michael@0 | 1724 | * incident on `u` and `v`. |
michael@0 | 1725 | */ |
michael@0 | 1726 | function undirEdgeId(u, v) { |
michael@0 | 1727 | return u < v |
michael@0 | 1728 | ? u.toString().length + ':' + u + '-' + v |
michael@0 | 1729 | : v.toString().length + ':' + v + '-' + u; |
michael@0 | 1730 | } |
michael@0 | 1731 | |
michael@0 | 1732 | function findConflicts(g, layering) { |
michael@0 | 1733 | var conflicts = {}, // Set of conflicting edge ids |
michael@0 | 1734 | pos = {}, // Position of node in its layer |
michael@0 | 1735 | prevLayer, |
michael@0 | 1736 | currLayer, |
michael@0 | 1737 | k0, // Position of the last inner segment in the previous layer |
michael@0 | 1738 | l, // Current position in the current layer (for iteration up to `l1`) |
michael@0 | 1739 | k1; // Position of the next inner segment in the previous layer or |
michael@0 | 1740 | // the position of the last element in the previous layer |
michael@0 | 1741 | |
michael@0 | 1742 | if (layering.length <= 2) return conflicts; |
michael@0 | 1743 | |
michael@0 | 1744 | function updateConflicts(v) { |
michael@0 | 1745 | var k = pos[v]; |
michael@0 | 1746 | if (k < k0 || k > k1) { |
michael@0 | 1747 | conflicts[undirEdgeId(currLayer[l], v)] = true; |
michael@0 | 1748 | } |
michael@0 | 1749 | } |
michael@0 | 1750 | |
michael@0 | 1751 | layering[1].forEach(function(u, i) { pos[u] = i; }); |
michael@0 | 1752 | for (var i = 1; i < layering.length - 1; ++i) { |
michael@0 | 1753 | prevLayer = layering[i]; |
michael@0 | 1754 | currLayer = layering[i+1]; |
michael@0 | 1755 | k0 = 0; |
michael@0 | 1756 | l = 0; |
michael@0 | 1757 | |
michael@0 | 1758 | // Scan current layer for next node that is incident to an inner segement |
michael@0 | 1759 | // between layering[i+1] and layering[i]. |
michael@0 | 1760 | for (var l1 = 0; l1 < currLayer.length; ++l1) { |
michael@0 | 1761 | var u = currLayer[l1]; // Next inner segment in the current layer or |
michael@0 | 1762 | // last node in the current layer |
michael@0 | 1763 | pos[u] = l1; |
michael@0 | 1764 | k1 = undefined; |
michael@0 | 1765 | |
michael@0 | 1766 | if (g.node(u).dummy) { |
michael@0 | 1767 | var uPred = g.predecessors(u)[0]; |
michael@0 | 1768 | // Note: In the case of self loops and sideways edges it is possible |
michael@0 | 1769 | // for a dummy not to have a predecessor. |
michael@0 | 1770 | if (uPred !== undefined && g.node(uPred).dummy) |
michael@0 | 1771 | k1 = pos[uPred]; |
michael@0 | 1772 | } |
michael@0 | 1773 | if (k1 === undefined && l1 === currLayer.length - 1) |
michael@0 | 1774 | k1 = prevLayer.length - 1; |
michael@0 | 1775 | |
michael@0 | 1776 | if (k1 !== undefined) { |
michael@0 | 1777 | for (; l <= l1; ++l) { |
michael@0 | 1778 | g.predecessors(currLayer[l]).forEach(updateConflicts); |
michael@0 | 1779 | } |
michael@0 | 1780 | k0 = k1; |
michael@0 | 1781 | } |
michael@0 | 1782 | } |
michael@0 | 1783 | } |
michael@0 | 1784 | |
michael@0 | 1785 | return conflicts; |
michael@0 | 1786 | } |
michael@0 | 1787 | |
michael@0 | 1788 | function verticalAlignment(g, layering, conflicts, relationship) { |
michael@0 | 1789 | var pos = {}, // Position for a node in its layer |
michael@0 | 1790 | root = {}, // Root of the block that the node participates in |
michael@0 | 1791 | align = {}; // Points to the next node in the block or, if the last |
michael@0 | 1792 | // element in the block, points to the first block's root |
michael@0 | 1793 | |
michael@0 | 1794 | layering.forEach(function(layer) { |
michael@0 | 1795 | layer.forEach(function(u, i) { |
michael@0 | 1796 | root[u] = u; |
michael@0 | 1797 | align[u] = u; |
michael@0 | 1798 | pos[u] = i; |
michael@0 | 1799 | }); |
michael@0 | 1800 | }); |
michael@0 | 1801 | |
michael@0 | 1802 | layering.forEach(function(layer) { |
michael@0 | 1803 | var prevIdx = -1; |
michael@0 | 1804 | layer.forEach(function(v) { |
michael@0 | 1805 | var related = g[relationship](v), // Adjacent nodes from the previous layer |
michael@0 | 1806 | mid; // The mid point in the related array |
michael@0 | 1807 | |
michael@0 | 1808 | if (related.length > 0) { |
michael@0 | 1809 | related.sort(function(x, y) { return pos[x] - pos[y]; }); |
michael@0 | 1810 | mid = (related.length - 1) / 2; |
michael@0 | 1811 | related.slice(Math.floor(mid), Math.ceil(mid) + 1).forEach(function(u) { |
michael@0 | 1812 | if (align[v] === v) { |
michael@0 | 1813 | if (!conflicts[undirEdgeId(u, v)] && prevIdx < pos[u]) { |
michael@0 | 1814 | align[u] = v; |
michael@0 | 1815 | align[v] = root[v] = root[u]; |
michael@0 | 1816 | prevIdx = pos[u]; |
michael@0 | 1817 | } |
michael@0 | 1818 | } |
michael@0 | 1819 | }); |
michael@0 | 1820 | } |
michael@0 | 1821 | }); |
michael@0 | 1822 | }); |
michael@0 | 1823 | |
michael@0 | 1824 | return { pos: pos, root: root, align: align }; |
michael@0 | 1825 | } |
michael@0 | 1826 | |
michael@0 | 1827 | // This function deviates from the standard BK algorithm in two ways. First |
michael@0 | 1828 | // it takes into account the size of the nodes. Second it includes a fix to |
michael@0 | 1829 | // the original algorithm that is described in Carstens, "Node and Label |
michael@0 | 1830 | // Placement in a Layered Layout Algorithm". |
michael@0 | 1831 | function horizontalCompaction(g, layering, pos, root, align) { |
michael@0 | 1832 | var sink = {}, // Mapping of node id -> sink node id for class |
michael@0 | 1833 | maybeShift = {}, // Mapping of sink node id -> { class node id, min shift } |
michael@0 | 1834 | shift = {}, // Mapping of sink node id -> shift |
michael@0 | 1835 | pred = {}, // Mapping of node id -> predecessor node (or null) |
michael@0 | 1836 | xs = {}; // Calculated X positions |
michael@0 | 1837 | |
michael@0 | 1838 | layering.forEach(function(layer) { |
michael@0 | 1839 | layer.forEach(function(u, i) { |
michael@0 | 1840 | sink[u] = u; |
michael@0 | 1841 | maybeShift[u] = {}; |
michael@0 | 1842 | if (i > 0) |
michael@0 | 1843 | pred[u] = layer[i - 1]; |
michael@0 | 1844 | }); |
michael@0 | 1845 | }); |
michael@0 | 1846 | |
michael@0 | 1847 | function updateShift(toShift, neighbor, delta) { |
michael@0 | 1848 | if (!(neighbor in maybeShift[toShift])) { |
michael@0 | 1849 | maybeShift[toShift][neighbor] = delta; |
michael@0 | 1850 | } else { |
michael@0 | 1851 | maybeShift[toShift][neighbor] = Math.min(maybeShift[toShift][neighbor], delta); |
michael@0 | 1852 | } |
michael@0 | 1853 | } |
michael@0 | 1854 | |
michael@0 | 1855 | function placeBlock(v) { |
michael@0 | 1856 | if (!(v in xs)) { |
michael@0 | 1857 | xs[v] = 0; |
michael@0 | 1858 | var w = v; |
michael@0 | 1859 | do { |
michael@0 | 1860 | if (pos[w] > 0) { |
michael@0 | 1861 | var u = root[pred[w]]; |
michael@0 | 1862 | placeBlock(u); |
michael@0 | 1863 | if (sink[v] === v) { |
michael@0 | 1864 | sink[v] = sink[u]; |
michael@0 | 1865 | } |
michael@0 | 1866 | var delta = sep(g, pred[w]) + sep(g, w); |
michael@0 | 1867 | if (sink[v] !== sink[u]) { |
michael@0 | 1868 | updateShift(sink[u], sink[v], xs[v] - xs[u] - delta); |
michael@0 | 1869 | } else { |
michael@0 | 1870 | xs[v] = Math.max(xs[v], xs[u] + delta); |
michael@0 | 1871 | } |
michael@0 | 1872 | } |
michael@0 | 1873 | w = align[w]; |
michael@0 | 1874 | } while (w !== v); |
michael@0 | 1875 | } |
michael@0 | 1876 | } |
michael@0 | 1877 | |
michael@0 | 1878 | // Root coordinates relative to sink |
michael@0 | 1879 | util.values(root).forEach(function(v) { |
michael@0 | 1880 | placeBlock(v); |
michael@0 | 1881 | }); |
michael@0 | 1882 | |
michael@0 | 1883 | // Absolute coordinates |
michael@0 | 1884 | // There is an assumption here that we've resolved shifts for any classes |
michael@0 | 1885 | // that begin at an earlier layer. We guarantee this by visiting layers in |
michael@0 | 1886 | // order. |
michael@0 | 1887 | layering.forEach(function(layer) { |
michael@0 | 1888 | layer.forEach(function(v) { |
michael@0 | 1889 | xs[v] = xs[root[v]]; |
michael@0 | 1890 | if (v === root[v] && v === sink[v]) { |
michael@0 | 1891 | var minShift = 0; |
michael@0 | 1892 | if (v in maybeShift && Object.keys(maybeShift[v]).length > 0) { |
michael@0 | 1893 | minShift = util.min(Object.keys(maybeShift[v]) |
michael@0 | 1894 | .map(function(u) { |
michael@0 | 1895 | return maybeShift[v][u] + (u in shift ? shift[u] : 0); |
michael@0 | 1896 | } |
michael@0 | 1897 | )); |
michael@0 | 1898 | } |
michael@0 | 1899 | shift[v] = minShift; |
michael@0 | 1900 | } |
michael@0 | 1901 | }); |
michael@0 | 1902 | }); |
michael@0 | 1903 | |
michael@0 | 1904 | layering.forEach(function(layer) { |
michael@0 | 1905 | layer.forEach(function(v) { |
michael@0 | 1906 | xs[v] += shift[sink[root[v]]] || 0; |
michael@0 | 1907 | }); |
michael@0 | 1908 | }); |
michael@0 | 1909 | |
michael@0 | 1910 | return xs; |
michael@0 | 1911 | } |
michael@0 | 1912 | |
michael@0 | 1913 | function findMinCoord(g, layering, xs) { |
michael@0 | 1914 | return util.min(layering.map(function(layer) { |
michael@0 | 1915 | var u = layer[0]; |
michael@0 | 1916 | return xs[u]; |
michael@0 | 1917 | })); |
michael@0 | 1918 | } |
michael@0 | 1919 | |
michael@0 | 1920 | function findMaxCoord(g, layering, xs) { |
michael@0 | 1921 | return util.max(layering.map(function(layer) { |
michael@0 | 1922 | var u = layer[layer.length - 1]; |
michael@0 | 1923 | return xs[u]; |
michael@0 | 1924 | })); |
michael@0 | 1925 | } |
michael@0 | 1926 | |
michael@0 | 1927 | function balance(g, layering, xss) { |
michael@0 | 1928 | var min = {}, // Min coordinate for the alignment |
michael@0 | 1929 | max = {}, // Max coordinate for the alginment |
michael@0 | 1930 | smallestAlignment, |
michael@0 | 1931 | shift = {}; // Amount to shift a given alignment |
michael@0 | 1932 | |
michael@0 | 1933 | function updateAlignment(v) { |
michael@0 | 1934 | xss[alignment][v] += shift[alignment]; |
michael@0 | 1935 | } |
michael@0 | 1936 | |
michael@0 | 1937 | var smallest = Number.POSITIVE_INFINITY; |
michael@0 | 1938 | for (var alignment in xss) { |
michael@0 | 1939 | var xs = xss[alignment]; |
michael@0 | 1940 | min[alignment] = findMinCoord(g, layering, xs); |
michael@0 | 1941 | max[alignment] = findMaxCoord(g, layering, xs); |
michael@0 | 1942 | var w = max[alignment] - min[alignment]; |
michael@0 | 1943 | if (w < smallest) { |
michael@0 | 1944 | smallest = w; |
michael@0 | 1945 | smallestAlignment = alignment; |
michael@0 | 1946 | } |
michael@0 | 1947 | } |
michael@0 | 1948 | |
michael@0 | 1949 | // Determine how much to adjust positioning for each alignment |
michael@0 | 1950 | ['u', 'd'].forEach(function(vertDir) { |
michael@0 | 1951 | ['l', 'r'].forEach(function(horizDir) { |
michael@0 | 1952 | var alignment = vertDir + horizDir; |
michael@0 | 1953 | shift[alignment] = horizDir === 'l' |
michael@0 | 1954 | ? min[smallestAlignment] - min[alignment] |
michael@0 | 1955 | : max[smallestAlignment] - max[alignment]; |
michael@0 | 1956 | }); |
michael@0 | 1957 | }); |
michael@0 | 1958 | |
michael@0 | 1959 | // Find average of medians for xss array |
michael@0 | 1960 | for (alignment in xss) { |
michael@0 | 1961 | g.eachNode(updateAlignment); |
michael@0 | 1962 | } |
michael@0 | 1963 | } |
michael@0 | 1964 | |
michael@0 | 1965 | function flipHorizontally(xs) { |
michael@0 | 1966 | for (var u in xs) { |
michael@0 | 1967 | xs[u] = -xs[u]; |
michael@0 | 1968 | } |
michael@0 | 1969 | } |
michael@0 | 1970 | |
michael@0 | 1971 | function reverseInnerOrder(layering) { |
michael@0 | 1972 | layering.forEach(function(layer) { |
michael@0 | 1973 | layer.reverse(); |
michael@0 | 1974 | }); |
michael@0 | 1975 | } |
michael@0 | 1976 | |
michael@0 | 1977 | function width(g, u) { |
michael@0 | 1978 | switch (g.graph().rankDir) { |
michael@0 | 1979 | case 'LR': return g.node(u).height; |
michael@0 | 1980 | case 'RL': return g.node(u).height; |
michael@0 | 1981 | default: return g.node(u).width; |
michael@0 | 1982 | } |
michael@0 | 1983 | } |
michael@0 | 1984 | |
michael@0 | 1985 | function height(g, u) { |
michael@0 | 1986 | switch(g.graph().rankDir) { |
michael@0 | 1987 | case 'LR': return g.node(u).width; |
michael@0 | 1988 | case 'RL': return g.node(u).width; |
michael@0 | 1989 | default: return g.node(u).height; |
michael@0 | 1990 | } |
michael@0 | 1991 | } |
michael@0 | 1992 | |
michael@0 | 1993 | function sep(g, u) { |
michael@0 | 1994 | if (config.universalSep !== null) { |
michael@0 | 1995 | return config.universalSep; |
michael@0 | 1996 | } |
michael@0 | 1997 | var w = width(g, u); |
michael@0 | 1998 | var s = g.node(u).dummy ? config.edgeSep : config.nodeSep; |
michael@0 | 1999 | return (w + s) / 2; |
michael@0 | 2000 | } |
michael@0 | 2001 | |
michael@0 | 2002 | function posX(g, u, x) { |
michael@0 | 2003 | if (g.graph().rankDir === 'LR' || g.graph().rankDir === 'RL') { |
michael@0 | 2004 | if (arguments.length < 3) { |
michael@0 | 2005 | return g.node(u).y; |
michael@0 | 2006 | } else { |
michael@0 | 2007 | g.node(u).y = x; |
michael@0 | 2008 | } |
michael@0 | 2009 | } else { |
michael@0 | 2010 | if (arguments.length < 3) { |
michael@0 | 2011 | return g.node(u).x; |
michael@0 | 2012 | } else { |
michael@0 | 2013 | g.node(u).x = x; |
michael@0 | 2014 | } |
michael@0 | 2015 | } |
michael@0 | 2016 | } |
michael@0 | 2017 | |
michael@0 | 2018 | function posXDebug(name, g, u, x) { |
michael@0 | 2019 | if (g.graph().rankDir === 'LR' || g.graph().rankDir === 'RL') { |
michael@0 | 2020 | if (arguments.length < 3) { |
michael@0 | 2021 | return g.node(u)[name]; |
michael@0 | 2022 | } else { |
michael@0 | 2023 | g.node(u)[name] = x; |
michael@0 | 2024 | } |
michael@0 | 2025 | } else { |
michael@0 | 2026 | if (arguments.length < 3) { |
michael@0 | 2027 | return g.node(u)[name]; |
michael@0 | 2028 | } else { |
michael@0 | 2029 | g.node(u)[name] = x; |
michael@0 | 2030 | } |
michael@0 | 2031 | } |
michael@0 | 2032 | } |
michael@0 | 2033 | |
michael@0 | 2034 | function posY(g, u, y) { |
michael@0 | 2035 | if (g.graph().rankDir === 'LR' || g.graph().rankDir === 'RL') { |
michael@0 | 2036 | if (arguments.length < 3) { |
michael@0 | 2037 | return g.node(u).x; |
michael@0 | 2038 | } else { |
michael@0 | 2039 | g.node(u).x = y; |
michael@0 | 2040 | } |
michael@0 | 2041 | } else { |
michael@0 | 2042 | if (arguments.length < 3) { |
michael@0 | 2043 | return g.node(u).y; |
michael@0 | 2044 | } else { |
michael@0 | 2045 | g.node(u).y = y; |
michael@0 | 2046 | } |
michael@0 | 2047 | } |
michael@0 | 2048 | } |
michael@0 | 2049 | |
michael@0 | 2050 | function debugPositioning(align, g, layering, xs) { |
michael@0 | 2051 | layering.forEach(function(l, li) { |
michael@0 | 2052 | var u, xU; |
michael@0 | 2053 | l.forEach(function(v) { |
michael@0 | 2054 | var xV = xs[v]; |
michael@0 | 2055 | if (u) { |
michael@0 | 2056 | var s = sep(g, u) + sep(g, v); |
michael@0 | 2057 | if (xV - xU < s) |
michael@0 | 2058 | console.log('Position phase: sep violation. Align: ' + align + '. Layer: ' + li + '. ' + |
michael@0 | 2059 | 'U: ' + u + ' V: ' + v + '. Actual sep: ' + (xV - xU) + ' Expected sep: ' + s); |
michael@0 | 2060 | } |
michael@0 | 2061 | u = v; |
michael@0 | 2062 | xU = xV; |
michael@0 | 2063 | }); |
michael@0 | 2064 | }); |
michael@0 | 2065 | } |
michael@0 | 2066 | }; |
michael@0 | 2067 | |
michael@0 | 2068 | },{"./util":26}],19:[function(require,module,exports){ |
michael@0 | 2069 | var util = require('./util'), |
michael@0 | 2070 | acyclic = require('./rank/acyclic'), |
michael@0 | 2071 | initRank = require('./rank/initRank'), |
michael@0 | 2072 | feasibleTree = require('./rank/feasibleTree'), |
michael@0 | 2073 | constraints = require('./rank/constraints'), |
michael@0 | 2074 | simplex = require('./rank/simplex'), |
michael@0 | 2075 | components = require('graphlib').alg.components, |
michael@0 | 2076 | filter = require('graphlib').filter; |
michael@0 | 2077 | |
michael@0 | 2078 | exports.run = run; |
michael@0 | 2079 | exports.restoreEdges = restoreEdges; |
michael@0 | 2080 | |
michael@0 | 2081 | /* |
michael@0 | 2082 | * Heuristic function that assigns a rank to each node of the input graph with |
michael@0 | 2083 | * the intent of minimizing edge lengths, while respecting the `minLen` |
michael@0 | 2084 | * attribute of incident edges. |
michael@0 | 2085 | * |
michael@0 | 2086 | * Prerequisites: |
michael@0 | 2087 | * |
michael@0 | 2088 | * * Each edge in the input graph must have an assigned 'minLen' attribute |
michael@0 | 2089 | */ |
michael@0 | 2090 | function run(g, useSimplex) { |
michael@0 | 2091 | expandSelfLoops(g); |
michael@0 | 2092 | |
michael@0 | 2093 | // If there are rank constraints on nodes, then build a new graph that |
michael@0 | 2094 | // encodes the constraints. |
michael@0 | 2095 | util.time('constraints.apply', constraints.apply)(g); |
michael@0 | 2096 | |
michael@0 | 2097 | expandSidewaysEdges(g); |
michael@0 | 2098 | |
michael@0 | 2099 | // Reverse edges to get an acyclic graph, we keep the graph in an acyclic |
michael@0 | 2100 | // state until the very end. |
michael@0 | 2101 | util.time('acyclic', acyclic)(g); |
michael@0 | 2102 | |
michael@0 | 2103 | // Convert the graph into a flat graph for ranking |
michael@0 | 2104 | var flatGraph = g.filterNodes(util.filterNonSubgraphs(g)); |
michael@0 | 2105 | |
michael@0 | 2106 | // Assign an initial ranking using DFS. |
michael@0 | 2107 | initRank(flatGraph); |
michael@0 | 2108 | |
michael@0 | 2109 | // For each component improve the assigned ranks. |
michael@0 | 2110 | components(flatGraph).forEach(function(cmpt) { |
michael@0 | 2111 | var subgraph = flatGraph.filterNodes(filter.nodesFromList(cmpt)); |
michael@0 | 2112 | rankComponent(subgraph, useSimplex); |
michael@0 | 2113 | }); |
michael@0 | 2114 | |
michael@0 | 2115 | // Relax original constraints |
michael@0 | 2116 | util.time('constraints.relax', constraints.relax(g)); |
michael@0 | 2117 | |
michael@0 | 2118 | // When handling nodes with constrained ranks it is possible to end up with |
michael@0 | 2119 | // edges that point to previous ranks. Most of the subsequent algorithms assume |
michael@0 | 2120 | // that edges are pointing to successive ranks only. Here we reverse any "back |
michael@0 | 2121 | // edges" and mark them as such. The acyclic algorithm will reverse them as a |
michael@0 | 2122 | // post processing step. |
michael@0 | 2123 | util.time('reorientEdges', reorientEdges)(g); |
michael@0 | 2124 | } |
michael@0 | 2125 | |
michael@0 | 2126 | function restoreEdges(g) { |
michael@0 | 2127 | acyclic.undo(g); |
michael@0 | 2128 | } |
michael@0 | 2129 | |
michael@0 | 2130 | /* |
michael@0 | 2131 | * Expand self loops into three dummy nodes. One will sit above the incident |
michael@0 | 2132 | * node, one will be at the same level, and one below. The result looks like: |
michael@0 | 2133 | * |
michael@0 | 2134 | * /--<--x--->--\ |
michael@0 | 2135 | * node y |
michael@0 | 2136 | * \--<--z--->--/ |
michael@0 | 2137 | * |
michael@0 | 2138 | * Dummy nodes x, y, z give us the shape of a loop and node y is where we place |
michael@0 | 2139 | * the label. |
michael@0 | 2140 | * |
michael@0 | 2141 | * TODO: consolidate knowledge of dummy node construction. |
michael@0 | 2142 | * TODO: support minLen = 2 |
michael@0 | 2143 | */ |
michael@0 | 2144 | function expandSelfLoops(g) { |
michael@0 | 2145 | g.eachEdge(function(e, u, v, a) { |
michael@0 | 2146 | if (u === v) { |
michael@0 | 2147 | var x = addDummyNode(g, e, u, v, a, 0, false), |
michael@0 | 2148 | y = addDummyNode(g, e, u, v, a, 1, true), |
michael@0 | 2149 | z = addDummyNode(g, e, u, v, a, 2, false); |
michael@0 | 2150 | g.addEdge(null, x, u, {minLen: 1, selfLoop: true}); |
michael@0 | 2151 | g.addEdge(null, x, y, {minLen: 1, selfLoop: true}); |
michael@0 | 2152 | g.addEdge(null, u, z, {minLen: 1, selfLoop: true}); |
michael@0 | 2153 | g.addEdge(null, y, z, {minLen: 1, selfLoop: true}); |
michael@0 | 2154 | g.delEdge(e); |
michael@0 | 2155 | } |
michael@0 | 2156 | }); |
michael@0 | 2157 | } |
michael@0 | 2158 | |
michael@0 | 2159 | function expandSidewaysEdges(g) { |
michael@0 | 2160 | g.eachEdge(function(e, u, v, a) { |
michael@0 | 2161 | if (u === v) { |
michael@0 | 2162 | var origEdge = a.originalEdge, |
michael@0 | 2163 | dummy = addDummyNode(g, origEdge.e, origEdge.u, origEdge.v, origEdge.value, 0, true); |
michael@0 | 2164 | g.addEdge(null, u, dummy, {minLen: 1}); |
michael@0 | 2165 | g.addEdge(null, dummy, v, {minLen: 1}); |
michael@0 | 2166 | g.delEdge(e); |
michael@0 | 2167 | } |
michael@0 | 2168 | }); |
michael@0 | 2169 | } |
michael@0 | 2170 | |
michael@0 | 2171 | function addDummyNode(g, e, u, v, a, index, isLabel) { |
michael@0 | 2172 | return g.addNode(null, { |
michael@0 | 2173 | width: isLabel ? a.width : 0, |
michael@0 | 2174 | height: isLabel ? a.height : 0, |
michael@0 | 2175 | edge: { id: e, source: u, target: v, attrs: a }, |
michael@0 | 2176 | dummy: true, |
michael@0 | 2177 | index: index |
michael@0 | 2178 | }); |
michael@0 | 2179 | } |
michael@0 | 2180 | |
michael@0 | 2181 | function reorientEdges(g) { |
michael@0 | 2182 | g.eachEdge(function(e, u, v, value) { |
michael@0 | 2183 | if (g.node(u).rank > g.node(v).rank) { |
michael@0 | 2184 | g.delEdge(e); |
michael@0 | 2185 | value.reversed = true; |
michael@0 | 2186 | g.addEdge(e, v, u, value); |
michael@0 | 2187 | } |
michael@0 | 2188 | }); |
michael@0 | 2189 | } |
michael@0 | 2190 | |
michael@0 | 2191 | function rankComponent(subgraph, useSimplex) { |
michael@0 | 2192 | var spanningTree = feasibleTree(subgraph); |
michael@0 | 2193 | |
michael@0 | 2194 | if (useSimplex) { |
michael@0 | 2195 | util.log(1, 'Using network simplex for ranking'); |
michael@0 | 2196 | simplex(subgraph, spanningTree); |
michael@0 | 2197 | } |
michael@0 | 2198 | normalize(subgraph); |
michael@0 | 2199 | } |
michael@0 | 2200 | |
michael@0 | 2201 | function normalize(g) { |
michael@0 | 2202 | var m = util.min(g.nodes().map(function(u) { return g.node(u).rank; })); |
michael@0 | 2203 | g.eachNode(function(u, node) { node.rank -= m; }); |
michael@0 | 2204 | } |
michael@0 | 2205 | |
michael@0 | 2206 | },{"./rank/acyclic":20,"./rank/constraints":21,"./rank/feasibleTree":22,"./rank/initRank":23,"./rank/simplex":25,"./util":26,"graphlib":28}],20:[function(require,module,exports){ |
michael@0 | 2207 | var util = require('../util'); |
michael@0 | 2208 | |
michael@0 | 2209 | module.exports = acyclic; |
michael@0 | 2210 | module.exports.undo = undo; |
michael@0 | 2211 | |
michael@0 | 2212 | /* |
michael@0 | 2213 | * This function takes a directed graph that may have cycles and reverses edges |
michael@0 | 2214 | * as appropriate to break these cycles. Each reversed edge is assigned a |
michael@0 | 2215 | * `reversed` attribute with the value `true`. |
michael@0 | 2216 | * |
michael@0 | 2217 | * There should be no self loops in the graph. |
michael@0 | 2218 | */ |
michael@0 | 2219 | function acyclic(g) { |
michael@0 | 2220 | var onStack = {}, |
michael@0 | 2221 | visited = {}, |
michael@0 | 2222 | reverseCount = 0; |
michael@0 | 2223 | |
michael@0 | 2224 | function dfs(u) { |
michael@0 | 2225 | if (u in visited) return; |
michael@0 | 2226 | visited[u] = onStack[u] = true; |
michael@0 | 2227 | g.outEdges(u).forEach(function(e) { |
michael@0 | 2228 | var t = g.target(e), |
michael@0 | 2229 | value; |
michael@0 | 2230 | |
michael@0 | 2231 | if (u === t) { |
michael@0 | 2232 | console.error('Warning: found self loop "' + e + '" for node "' + u + '"'); |
michael@0 | 2233 | } else if (t in onStack) { |
michael@0 | 2234 | value = g.edge(e); |
michael@0 | 2235 | g.delEdge(e); |
michael@0 | 2236 | value.reversed = true; |
michael@0 | 2237 | ++reverseCount; |
michael@0 | 2238 | g.addEdge(e, t, u, value); |
michael@0 | 2239 | } else { |
michael@0 | 2240 | dfs(t); |
michael@0 | 2241 | } |
michael@0 | 2242 | }); |
michael@0 | 2243 | |
michael@0 | 2244 | delete onStack[u]; |
michael@0 | 2245 | } |
michael@0 | 2246 | |
michael@0 | 2247 | g.eachNode(function(u) { dfs(u); }); |
michael@0 | 2248 | |
michael@0 | 2249 | util.log(2, 'Acyclic Phase: reversed ' + reverseCount + ' edge(s)'); |
michael@0 | 2250 | |
michael@0 | 2251 | return reverseCount; |
michael@0 | 2252 | } |
michael@0 | 2253 | |
michael@0 | 2254 | /* |
michael@0 | 2255 | * Given a graph that has had the acyclic operation applied, this function |
michael@0 | 2256 | * undoes that operation. More specifically, any edge with the `reversed` |
michael@0 | 2257 | * attribute is again reversed to restore the original direction of the edge. |
michael@0 | 2258 | */ |
michael@0 | 2259 | function undo(g) { |
michael@0 | 2260 | g.eachEdge(function(e, s, t, a) { |
michael@0 | 2261 | if (a.reversed) { |
michael@0 | 2262 | delete a.reversed; |
michael@0 | 2263 | g.delEdge(e); |
michael@0 | 2264 | g.addEdge(e, t, s, a); |
michael@0 | 2265 | } |
michael@0 | 2266 | }); |
michael@0 | 2267 | } |
michael@0 | 2268 | |
michael@0 | 2269 | },{"../util":26}],21:[function(require,module,exports){ |
michael@0 | 2270 | exports.apply = function(g) { |
michael@0 | 2271 | function dfs(sg) { |
michael@0 | 2272 | var rankSets = {}; |
michael@0 | 2273 | g.children(sg).forEach(function(u) { |
michael@0 | 2274 | if (g.children(u).length) { |
michael@0 | 2275 | dfs(u); |
michael@0 | 2276 | return; |
michael@0 | 2277 | } |
michael@0 | 2278 | |
michael@0 | 2279 | var value = g.node(u), |
michael@0 | 2280 | prefRank = value.prefRank; |
michael@0 | 2281 | if (prefRank !== undefined) { |
michael@0 | 2282 | if (!checkSupportedPrefRank(prefRank)) { return; } |
michael@0 | 2283 | |
michael@0 | 2284 | if (!(prefRank in rankSets)) { |
michael@0 | 2285 | rankSets.prefRank = [u]; |
michael@0 | 2286 | } else { |
michael@0 | 2287 | rankSets.prefRank.push(u); |
michael@0 | 2288 | } |
michael@0 | 2289 | |
michael@0 | 2290 | var newU = rankSets[prefRank]; |
michael@0 | 2291 | if (newU === undefined) { |
michael@0 | 2292 | newU = rankSets[prefRank] = g.addNode(null, { originalNodes: [] }); |
michael@0 | 2293 | g.parent(newU, sg); |
michael@0 | 2294 | } |
michael@0 | 2295 | |
michael@0 | 2296 | redirectInEdges(g, u, newU, prefRank === 'min'); |
michael@0 | 2297 | redirectOutEdges(g, u, newU, prefRank === 'max'); |
michael@0 | 2298 | |
michael@0 | 2299 | // Save original node and remove it from reduced graph |
michael@0 | 2300 | g.node(newU).originalNodes.push({ u: u, value: value, parent: sg }); |
michael@0 | 2301 | g.delNode(u); |
michael@0 | 2302 | } |
michael@0 | 2303 | }); |
michael@0 | 2304 | |
michael@0 | 2305 | addLightEdgesFromMinNode(g, sg, rankSets.min); |
michael@0 | 2306 | addLightEdgesToMaxNode(g, sg, rankSets.max); |
michael@0 | 2307 | } |
michael@0 | 2308 | |
michael@0 | 2309 | dfs(null); |
michael@0 | 2310 | }; |
michael@0 | 2311 | |
michael@0 | 2312 | function checkSupportedPrefRank(prefRank) { |
michael@0 | 2313 | if (prefRank !== 'min' && prefRank !== 'max' && prefRank.indexOf('same_') !== 0) { |
michael@0 | 2314 | console.error('Unsupported rank type: ' + prefRank); |
michael@0 | 2315 | return false; |
michael@0 | 2316 | } |
michael@0 | 2317 | return true; |
michael@0 | 2318 | } |
michael@0 | 2319 | |
michael@0 | 2320 | function redirectInEdges(g, u, newU, reverse) { |
michael@0 | 2321 | g.inEdges(u).forEach(function(e) { |
michael@0 | 2322 | var origValue = g.edge(e), |
michael@0 | 2323 | value; |
michael@0 | 2324 | if (origValue.originalEdge) { |
michael@0 | 2325 | value = origValue; |
michael@0 | 2326 | } else { |
michael@0 | 2327 | value = { |
michael@0 | 2328 | originalEdge: { e: e, u: g.source(e), v: g.target(e), value: origValue }, |
michael@0 | 2329 | minLen: g.edge(e).minLen |
michael@0 | 2330 | }; |
michael@0 | 2331 | } |
michael@0 | 2332 | |
michael@0 | 2333 | // Do not reverse edges for self-loops. |
michael@0 | 2334 | if (origValue.selfLoop) { |
michael@0 | 2335 | reverse = false; |
michael@0 | 2336 | } |
michael@0 | 2337 | |
michael@0 | 2338 | if (reverse) { |
michael@0 | 2339 | // Ensure that all edges to min are reversed |
michael@0 | 2340 | g.addEdge(null, newU, g.source(e), value); |
michael@0 | 2341 | value.reversed = true; |
michael@0 | 2342 | } else { |
michael@0 | 2343 | g.addEdge(null, g.source(e), newU, value); |
michael@0 | 2344 | } |
michael@0 | 2345 | }); |
michael@0 | 2346 | } |
michael@0 | 2347 | |
michael@0 | 2348 | function redirectOutEdges(g, u, newU, reverse) { |
michael@0 | 2349 | g.outEdges(u).forEach(function(e) { |
michael@0 | 2350 | var origValue = g.edge(e), |
michael@0 | 2351 | value; |
michael@0 | 2352 | if (origValue.originalEdge) { |
michael@0 | 2353 | value = origValue; |
michael@0 | 2354 | } else { |
michael@0 | 2355 | value = { |
michael@0 | 2356 | originalEdge: { e: e, u: g.source(e), v: g.target(e), value: origValue }, |
michael@0 | 2357 | minLen: g.edge(e).minLen |
michael@0 | 2358 | }; |
michael@0 | 2359 | } |
michael@0 | 2360 | |
michael@0 | 2361 | // Do not reverse edges for self-loops. |
michael@0 | 2362 | if (origValue.selfLoop) { |
michael@0 | 2363 | reverse = false; |
michael@0 | 2364 | } |
michael@0 | 2365 | |
michael@0 | 2366 | if (reverse) { |
michael@0 | 2367 | // Ensure that all edges from max are reversed |
michael@0 | 2368 | g.addEdge(null, g.target(e), newU, value); |
michael@0 | 2369 | value.reversed = true; |
michael@0 | 2370 | } else { |
michael@0 | 2371 | g.addEdge(null, newU, g.target(e), value); |
michael@0 | 2372 | } |
michael@0 | 2373 | }); |
michael@0 | 2374 | } |
michael@0 | 2375 | |
michael@0 | 2376 | function addLightEdgesFromMinNode(g, sg, minNode) { |
michael@0 | 2377 | if (minNode !== undefined) { |
michael@0 | 2378 | g.children(sg).forEach(function(u) { |
michael@0 | 2379 | // The dummy check ensures we don't add an edge if the node is involved |
michael@0 | 2380 | // in a self loop or sideways edge. |
michael@0 | 2381 | if (u !== minNode && !g.outEdges(minNode, u).length && !g.node(u).dummy) { |
michael@0 | 2382 | g.addEdge(null, minNode, u, { minLen: 0 }); |
michael@0 | 2383 | } |
michael@0 | 2384 | }); |
michael@0 | 2385 | } |
michael@0 | 2386 | } |
michael@0 | 2387 | |
michael@0 | 2388 | function addLightEdgesToMaxNode(g, sg, maxNode) { |
michael@0 | 2389 | if (maxNode !== undefined) { |
michael@0 | 2390 | g.children(sg).forEach(function(u) { |
michael@0 | 2391 | // The dummy check ensures we don't add an edge if the node is involved |
michael@0 | 2392 | // in a self loop or sideways edge. |
michael@0 | 2393 | if (u !== maxNode && !g.outEdges(u, maxNode).length && !g.node(u).dummy) { |
michael@0 | 2394 | g.addEdge(null, u, maxNode, { minLen: 0 }); |
michael@0 | 2395 | } |
michael@0 | 2396 | }); |
michael@0 | 2397 | } |
michael@0 | 2398 | } |
michael@0 | 2399 | |
michael@0 | 2400 | /* |
michael@0 | 2401 | * This function "relaxes" the constraints applied previously by the "apply" |
michael@0 | 2402 | * function. It expands any nodes that were collapsed and assigns the rank of |
michael@0 | 2403 | * the collapsed node to each of the expanded nodes. It also restores the |
michael@0 | 2404 | * original edges and removes any dummy edges pointing at the collapsed nodes. |
michael@0 | 2405 | * |
michael@0 | 2406 | * Note that the process of removing collapsed nodes also removes dummy edges |
michael@0 | 2407 | * automatically. |
michael@0 | 2408 | */ |
michael@0 | 2409 | exports.relax = function(g) { |
michael@0 | 2410 | // Save original edges |
michael@0 | 2411 | var originalEdges = []; |
michael@0 | 2412 | g.eachEdge(function(e, u, v, value) { |
michael@0 | 2413 | var originalEdge = value.originalEdge; |
michael@0 | 2414 | if (originalEdge) { |
michael@0 | 2415 | originalEdges.push(originalEdge); |
michael@0 | 2416 | } |
michael@0 | 2417 | }); |
michael@0 | 2418 | |
michael@0 | 2419 | // Expand collapsed nodes |
michael@0 | 2420 | g.eachNode(function(u, value) { |
michael@0 | 2421 | var originalNodes = value.originalNodes; |
michael@0 | 2422 | if (originalNodes) { |
michael@0 | 2423 | originalNodes.forEach(function(originalNode) { |
michael@0 | 2424 | originalNode.value.rank = value.rank; |
michael@0 | 2425 | g.addNode(originalNode.u, originalNode.value); |
michael@0 | 2426 | g.parent(originalNode.u, originalNode.parent); |
michael@0 | 2427 | }); |
michael@0 | 2428 | g.delNode(u); |
michael@0 | 2429 | } |
michael@0 | 2430 | }); |
michael@0 | 2431 | |
michael@0 | 2432 | // Restore original edges |
michael@0 | 2433 | originalEdges.forEach(function(edge) { |
michael@0 | 2434 | g.addEdge(edge.e, edge.u, edge.v, edge.value); |
michael@0 | 2435 | }); |
michael@0 | 2436 | }; |
michael@0 | 2437 | |
michael@0 | 2438 | },{}],22:[function(require,module,exports){ |
michael@0 | 2439 | /* jshint -W079 */ |
michael@0 | 2440 | var Set = require('cp-data').Set, |
michael@0 | 2441 | /* jshint +W079 */ |
michael@0 | 2442 | Digraph = require('graphlib').Digraph, |
michael@0 | 2443 | util = require('../util'); |
michael@0 | 2444 | |
michael@0 | 2445 | module.exports = feasibleTree; |
michael@0 | 2446 | |
michael@0 | 2447 | /* |
michael@0 | 2448 | * Given an acyclic graph with each node assigned a `rank` attribute, this |
michael@0 | 2449 | * function constructs and returns a spanning tree. This function may reduce |
michael@0 | 2450 | * the length of some edges from the initial rank assignment while maintaining |
michael@0 | 2451 | * the `minLen` specified by each edge. |
michael@0 | 2452 | * |
michael@0 | 2453 | * Prerequisites: |
michael@0 | 2454 | * |
michael@0 | 2455 | * * The input graph is acyclic |
michael@0 | 2456 | * * Each node in the input graph has an assigned `rank` attribute |
michael@0 | 2457 | * * Each edge in the input graph has an assigned `minLen` attribute |
michael@0 | 2458 | * |
michael@0 | 2459 | * Outputs: |
michael@0 | 2460 | * |
michael@0 | 2461 | * A feasible spanning tree for the input graph (i.e. a spanning tree that |
michael@0 | 2462 | * respects each graph edge's `minLen` attribute) represented as a Digraph with |
michael@0 | 2463 | * a `root` attribute on graph. |
michael@0 | 2464 | * |
michael@0 | 2465 | * Nodes have the same id and value as that in the input graph. |
michael@0 | 2466 | * |
michael@0 | 2467 | * Edges in the tree have arbitrarily assigned ids. The attributes for edges |
michael@0 | 2468 | * include `reversed`. `reversed` indicates that the edge is a |
michael@0 | 2469 | * back edge in the input graph. |
michael@0 | 2470 | */ |
michael@0 | 2471 | function feasibleTree(g) { |
michael@0 | 2472 | var remaining = new Set(g.nodes()), |
michael@0 | 2473 | tree = new Digraph(); |
michael@0 | 2474 | |
michael@0 | 2475 | if (remaining.size() === 1) { |
michael@0 | 2476 | var root = g.nodes()[0]; |
michael@0 | 2477 | tree.addNode(root, {}); |
michael@0 | 2478 | tree.graph({ root: root }); |
michael@0 | 2479 | return tree; |
michael@0 | 2480 | } |
michael@0 | 2481 | |
michael@0 | 2482 | function addTightEdges(v) { |
michael@0 | 2483 | var continueToScan = true; |
michael@0 | 2484 | g.predecessors(v).forEach(function(u) { |
michael@0 | 2485 | if (remaining.has(u) && !slack(g, u, v)) { |
michael@0 | 2486 | if (remaining.has(v)) { |
michael@0 | 2487 | tree.addNode(v, {}); |
michael@0 | 2488 | remaining.remove(v); |
michael@0 | 2489 | tree.graph({ root: v }); |
michael@0 | 2490 | } |
michael@0 | 2491 | |
michael@0 | 2492 | tree.addNode(u, {}); |
michael@0 | 2493 | tree.addEdge(null, u, v, { reversed: true }); |
michael@0 | 2494 | remaining.remove(u); |
michael@0 | 2495 | addTightEdges(u); |
michael@0 | 2496 | continueToScan = false; |
michael@0 | 2497 | } |
michael@0 | 2498 | }); |
michael@0 | 2499 | |
michael@0 | 2500 | g.successors(v).forEach(function(w) { |
michael@0 | 2501 | if (remaining.has(w) && !slack(g, v, w)) { |
michael@0 | 2502 | if (remaining.has(v)) { |
michael@0 | 2503 | tree.addNode(v, {}); |
michael@0 | 2504 | remaining.remove(v); |
michael@0 | 2505 | tree.graph({ root: v }); |
michael@0 | 2506 | } |
michael@0 | 2507 | |
michael@0 | 2508 | tree.addNode(w, {}); |
michael@0 | 2509 | tree.addEdge(null, v, w, {}); |
michael@0 | 2510 | remaining.remove(w); |
michael@0 | 2511 | addTightEdges(w); |
michael@0 | 2512 | continueToScan = false; |
michael@0 | 2513 | } |
michael@0 | 2514 | }); |
michael@0 | 2515 | return continueToScan; |
michael@0 | 2516 | } |
michael@0 | 2517 | |
michael@0 | 2518 | function createTightEdge() { |
michael@0 | 2519 | var minSlack = Number.MAX_VALUE; |
michael@0 | 2520 | remaining.keys().forEach(function(v) { |
michael@0 | 2521 | g.predecessors(v).forEach(function(u) { |
michael@0 | 2522 | if (!remaining.has(u)) { |
michael@0 | 2523 | var edgeSlack = slack(g, u, v); |
michael@0 | 2524 | if (Math.abs(edgeSlack) < Math.abs(minSlack)) { |
michael@0 | 2525 | minSlack = -edgeSlack; |
michael@0 | 2526 | } |
michael@0 | 2527 | } |
michael@0 | 2528 | }); |
michael@0 | 2529 | |
michael@0 | 2530 | g.successors(v).forEach(function(w) { |
michael@0 | 2531 | if (!remaining.has(w)) { |
michael@0 | 2532 | var edgeSlack = slack(g, v, w); |
michael@0 | 2533 | if (Math.abs(edgeSlack) < Math.abs(minSlack)) { |
michael@0 | 2534 | minSlack = edgeSlack; |
michael@0 | 2535 | } |
michael@0 | 2536 | } |
michael@0 | 2537 | }); |
michael@0 | 2538 | }); |
michael@0 | 2539 | |
michael@0 | 2540 | tree.eachNode(function(u) { g.node(u).rank -= minSlack; }); |
michael@0 | 2541 | } |
michael@0 | 2542 | |
michael@0 | 2543 | while (remaining.size()) { |
michael@0 | 2544 | var nodesToSearch = !tree.order() ? remaining.keys() : tree.nodes(); |
michael@0 | 2545 | for (var i = 0, il = nodesToSearch.length; |
michael@0 | 2546 | i < il && addTightEdges(nodesToSearch[i]); |
michael@0 | 2547 | ++i); |
michael@0 | 2548 | if (remaining.size()) { |
michael@0 | 2549 | createTightEdge(); |
michael@0 | 2550 | } |
michael@0 | 2551 | } |
michael@0 | 2552 | |
michael@0 | 2553 | return tree; |
michael@0 | 2554 | } |
michael@0 | 2555 | |
michael@0 | 2556 | function slack(g, u, v) { |
michael@0 | 2557 | var rankDiff = g.node(v).rank - g.node(u).rank; |
michael@0 | 2558 | var maxMinLen = util.max(g.outEdges(u, v) |
michael@0 | 2559 | .map(function(e) { return g.edge(e).minLen; })); |
michael@0 | 2560 | return rankDiff - maxMinLen; |
michael@0 | 2561 | } |
michael@0 | 2562 | |
michael@0 | 2563 | },{"../util":26,"cp-data":5,"graphlib":28}],23:[function(require,module,exports){ |
michael@0 | 2564 | var util = require('../util'), |
michael@0 | 2565 | topsort = require('graphlib').alg.topsort; |
michael@0 | 2566 | |
michael@0 | 2567 | module.exports = initRank; |
michael@0 | 2568 | |
michael@0 | 2569 | /* |
michael@0 | 2570 | * Assigns a `rank` attribute to each node in the input graph and ensures that |
michael@0 | 2571 | * this rank respects the `minLen` attribute of incident edges. |
michael@0 | 2572 | * |
michael@0 | 2573 | * Prerequisites: |
michael@0 | 2574 | * |
michael@0 | 2575 | * * The input graph must be acyclic |
michael@0 | 2576 | * * Each edge in the input graph must have an assigned 'minLen' attribute |
michael@0 | 2577 | */ |
michael@0 | 2578 | function initRank(g) { |
michael@0 | 2579 | var sorted = topsort(g); |
michael@0 | 2580 | |
michael@0 | 2581 | sorted.forEach(function(u) { |
michael@0 | 2582 | var inEdges = g.inEdges(u); |
michael@0 | 2583 | if (inEdges.length === 0) { |
michael@0 | 2584 | g.node(u).rank = 0; |
michael@0 | 2585 | return; |
michael@0 | 2586 | } |
michael@0 | 2587 | |
michael@0 | 2588 | var minLens = inEdges.map(function(e) { |
michael@0 | 2589 | return g.node(g.source(e)).rank + g.edge(e).minLen; |
michael@0 | 2590 | }); |
michael@0 | 2591 | g.node(u).rank = util.max(minLens); |
michael@0 | 2592 | }); |
michael@0 | 2593 | } |
michael@0 | 2594 | |
michael@0 | 2595 | },{"../util":26,"graphlib":28}],24:[function(require,module,exports){ |
michael@0 | 2596 | module.exports = { |
michael@0 | 2597 | slack: slack |
michael@0 | 2598 | }; |
michael@0 | 2599 | |
michael@0 | 2600 | /* |
michael@0 | 2601 | * A helper to calculate the slack between two nodes (`u` and `v`) given a |
michael@0 | 2602 | * `minLen` constraint. The slack represents how much the distance between `u` |
michael@0 | 2603 | * and `v` could shrink while maintaining the `minLen` constraint. If the value |
michael@0 | 2604 | * is negative then the constraint is currently violated. |
michael@0 | 2605 | * |
michael@0 | 2606 | This function requires that `u` and `v` are in `graph` and they both have a |
michael@0 | 2607 | `rank` attribute. |
michael@0 | 2608 | */ |
michael@0 | 2609 | function slack(graph, u, v, minLen) { |
michael@0 | 2610 | return Math.abs(graph.node(u).rank - graph.node(v).rank) - minLen; |
michael@0 | 2611 | } |
michael@0 | 2612 | |
michael@0 | 2613 | },{}],25:[function(require,module,exports){ |
michael@0 | 2614 | var util = require('../util'), |
michael@0 | 2615 | rankUtil = require('./rankUtil'); |
michael@0 | 2616 | |
michael@0 | 2617 | module.exports = simplex; |
michael@0 | 2618 | |
michael@0 | 2619 | function simplex(graph, spanningTree) { |
michael@0 | 2620 | // The network simplex algorithm repeatedly replaces edges of |
michael@0 | 2621 | // the spanning tree with negative cut values until no such |
michael@0 | 2622 | // edge exists. |
michael@0 | 2623 | initCutValues(graph, spanningTree); |
michael@0 | 2624 | while (true) { |
michael@0 | 2625 | var e = leaveEdge(spanningTree); |
michael@0 | 2626 | if (e === null) break; |
michael@0 | 2627 | var f = enterEdge(graph, spanningTree, e); |
michael@0 | 2628 | exchange(graph, spanningTree, e, f); |
michael@0 | 2629 | } |
michael@0 | 2630 | } |
michael@0 | 2631 | |
michael@0 | 2632 | /* |
michael@0 | 2633 | * Set the cut values of edges in the spanning tree by a depth-first |
michael@0 | 2634 | * postorder traversal. The cut value corresponds to the cost, in |
michael@0 | 2635 | * terms of a ranking's edge length sum, of lengthening an edge. |
michael@0 | 2636 | * Negative cut values typically indicate edges that would yield a |
michael@0 | 2637 | * smaller edge length sum if they were lengthened. |
michael@0 | 2638 | */ |
michael@0 | 2639 | function initCutValues(graph, spanningTree) { |
michael@0 | 2640 | computeLowLim(spanningTree); |
michael@0 | 2641 | |
michael@0 | 2642 | spanningTree.eachEdge(function(id, u, v, treeValue) { |
michael@0 | 2643 | treeValue.cutValue = 0; |
michael@0 | 2644 | }); |
michael@0 | 2645 | |
michael@0 | 2646 | // Propagate cut values up the tree. |
michael@0 | 2647 | function dfs(n) { |
michael@0 | 2648 | var children = spanningTree.successors(n); |
michael@0 | 2649 | for (var c in children) { |
michael@0 | 2650 | var child = children[c]; |
michael@0 | 2651 | dfs(child); |
michael@0 | 2652 | } |
michael@0 | 2653 | if (n !== spanningTree.graph().root) { |
michael@0 | 2654 | setCutValue(graph, spanningTree, n); |
michael@0 | 2655 | } |
michael@0 | 2656 | } |
michael@0 | 2657 | dfs(spanningTree.graph().root); |
michael@0 | 2658 | } |
michael@0 | 2659 | |
michael@0 | 2660 | /* |
michael@0 | 2661 | * Perform a DFS postorder traversal, labeling each node v with |
michael@0 | 2662 | * its traversal order 'lim(v)' and the minimum traversal number |
michael@0 | 2663 | * of any of its descendants 'low(v)'. This provides an efficient |
michael@0 | 2664 | * way to test whether u is an ancestor of v since |
michael@0 | 2665 | * low(u) <= lim(v) <= lim(u) if and only if u is an ancestor. |
michael@0 | 2666 | */ |
michael@0 | 2667 | function computeLowLim(tree) { |
michael@0 | 2668 | var postOrderNum = 0; |
michael@0 | 2669 | |
michael@0 | 2670 | function dfs(n) { |
michael@0 | 2671 | var children = tree.successors(n); |
michael@0 | 2672 | var low = postOrderNum; |
michael@0 | 2673 | for (var c in children) { |
michael@0 | 2674 | var child = children[c]; |
michael@0 | 2675 | dfs(child); |
michael@0 | 2676 | low = Math.min(low, tree.node(child).low); |
michael@0 | 2677 | } |
michael@0 | 2678 | tree.node(n).low = low; |
michael@0 | 2679 | tree.node(n).lim = postOrderNum++; |
michael@0 | 2680 | } |
michael@0 | 2681 | |
michael@0 | 2682 | dfs(tree.graph().root); |
michael@0 | 2683 | } |
michael@0 | 2684 | |
michael@0 | 2685 | /* |
michael@0 | 2686 | * To compute the cut value of the edge parent -> child, we consider |
michael@0 | 2687 | * it and any other graph edges to or from the child. |
michael@0 | 2688 | * parent |
michael@0 | 2689 | * | |
michael@0 | 2690 | * child |
michael@0 | 2691 | * / \ |
michael@0 | 2692 | * u v |
michael@0 | 2693 | */ |
michael@0 | 2694 | function setCutValue(graph, tree, child) { |
michael@0 | 2695 | var parentEdge = tree.inEdges(child)[0]; |
michael@0 | 2696 | |
michael@0 | 2697 | // List of child's children in the spanning tree. |
michael@0 | 2698 | var grandchildren = []; |
michael@0 | 2699 | var grandchildEdges = tree.outEdges(child); |
michael@0 | 2700 | for (var gce in grandchildEdges) { |
michael@0 | 2701 | grandchildren.push(tree.target(grandchildEdges[gce])); |
michael@0 | 2702 | } |
michael@0 | 2703 | |
michael@0 | 2704 | var cutValue = 0; |
michael@0 | 2705 | |
michael@0 | 2706 | // TODO: Replace unit increment/decrement with edge weights. |
michael@0 | 2707 | var E = 0; // Edges from child to grandchild's subtree. |
michael@0 | 2708 | var F = 0; // Edges to child from grandchild's subtree. |
michael@0 | 2709 | var G = 0; // Edges from child to nodes outside of child's subtree. |
michael@0 | 2710 | var H = 0; // Edges from nodes outside of child's subtree to child. |
michael@0 | 2711 | |
michael@0 | 2712 | // Consider all graph edges from child. |
michael@0 | 2713 | var outEdges = graph.outEdges(child); |
michael@0 | 2714 | var gc; |
michael@0 | 2715 | for (var oe in outEdges) { |
michael@0 | 2716 | var succ = graph.target(outEdges[oe]); |
michael@0 | 2717 | for (gc in grandchildren) { |
michael@0 | 2718 | if (inSubtree(tree, succ, grandchildren[gc])) { |
michael@0 | 2719 | E++; |
michael@0 | 2720 | } |
michael@0 | 2721 | } |
michael@0 | 2722 | if (!inSubtree(tree, succ, child)) { |
michael@0 | 2723 | G++; |
michael@0 | 2724 | } |
michael@0 | 2725 | } |
michael@0 | 2726 | |
michael@0 | 2727 | // Consider all graph edges to child. |
michael@0 | 2728 | var inEdges = graph.inEdges(child); |
michael@0 | 2729 | for (var ie in inEdges) { |
michael@0 | 2730 | var pred = graph.source(inEdges[ie]); |
michael@0 | 2731 | for (gc in grandchildren) { |
michael@0 | 2732 | if (inSubtree(tree, pred, grandchildren[gc])) { |
michael@0 | 2733 | F++; |
michael@0 | 2734 | } |
michael@0 | 2735 | } |
michael@0 | 2736 | if (!inSubtree(tree, pred, child)) { |
michael@0 | 2737 | H++; |
michael@0 | 2738 | } |
michael@0 | 2739 | } |
michael@0 | 2740 | |
michael@0 | 2741 | // Contributions depend on the alignment of the parent -> child edge |
michael@0 | 2742 | // and the child -> u or v edges. |
michael@0 | 2743 | var grandchildCutSum = 0; |
michael@0 | 2744 | for (gc in grandchildren) { |
michael@0 | 2745 | var cv = tree.edge(grandchildEdges[gc]).cutValue; |
michael@0 | 2746 | if (!tree.edge(grandchildEdges[gc]).reversed) { |
michael@0 | 2747 | grandchildCutSum += cv; |
michael@0 | 2748 | } else { |
michael@0 | 2749 | grandchildCutSum -= cv; |
michael@0 | 2750 | } |
michael@0 | 2751 | } |
michael@0 | 2752 | |
michael@0 | 2753 | if (!tree.edge(parentEdge).reversed) { |
michael@0 | 2754 | cutValue += grandchildCutSum - E + F - G + H; |
michael@0 | 2755 | } else { |
michael@0 | 2756 | cutValue -= grandchildCutSum - E + F - G + H; |
michael@0 | 2757 | } |
michael@0 | 2758 | |
michael@0 | 2759 | tree.edge(parentEdge).cutValue = cutValue; |
michael@0 | 2760 | } |
michael@0 | 2761 | |
michael@0 | 2762 | /* |
michael@0 | 2763 | * Return whether n is a node in the subtree with the given |
michael@0 | 2764 | * root. |
michael@0 | 2765 | */ |
michael@0 | 2766 | function inSubtree(tree, n, root) { |
michael@0 | 2767 | return (tree.node(root).low <= tree.node(n).lim && |
michael@0 | 2768 | tree.node(n).lim <= tree.node(root).lim); |
michael@0 | 2769 | } |
michael@0 | 2770 | |
michael@0 | 2771 | /* |
michael@0 | 2772 | * Return an edge from the tree with a negative cut value, or null if there |
michael@0 | 2773 | * is none. |
michael@0 | 2774 | */ |
michael@0 | 2775 | function leaveEdge(tree) { |
michael@0 | 2776 | var edges = tree.edges(); |
michael@0 | 2777 | for (var n in edges) { |
michael@0 | 2778 | var e = edges[n]; |
michael@0 | 2779 | var treeValue = tree.edge(e); |
michael@0 | 2780 | if (treeValue.cutValue < 0) { |
michael@0 | 2781 | return e; |
michael@0 | 2782 | } |
michael@0 | 2783 | } |
michael@0 | 2784 | return null; |
michael@0 | 2785 | } |
michael@0 | 2786 | |
michael@0 | 2787 | /* |
michael@0 | 2788 | * The edge e should be an edge in the tree, with an underlying edge |
michael@0 | 2789 | * in the graph, with a negative cut value. Of the two nodes incident |
michael@0 | 2790 | * on the edge, take the lower one. enterEdge returns an edge with |
michael@0 | 2791 | * minimum slack going from outside of that node's subtree to inside |
michael@0 | 2792 | * of that node's subtree. |
michael@0 | 2793 | */ |
michael@0 | 2794 | function enterEdge(graph, tree, e) { |
michael@0 | 2795 | var source = tree.source(e); |
michael@0 | 2796 | var target = tree.target(e); |
michael@0 | 2797 | var lower = tree.node(target).lim < tree.node(source).lim ? target : source; |
michael@0 | 2798 | |
michael@0 | 2799 | // Is the tree edge aligned with the graph edge? |
michael@0 | 2800 | var aligned = !tree.edge(e).reversed; |
michael@0 | 2801 | |
michael@0 | 2802 | var minSlack = Number.POSITIVE_INFINITY; |
michael@0 | 2803 | var minSlackEdge; |
michael@0 | 2804 | if (aligned) { |
michael@0 | 2805 | graph.eachEdge(function(id, u, v, value) { |
michael@0 | 2806 | if (id !== e && inSubtree(tree, u, lower) && !inSubtree(tree, v, lower)) { |
michael@0 | 2807 | var slack = rankUtil.slack(graph, u, v, value.minLen); |
michael@0 | 2808 | if (slack < minSlack) { |
michael@0 | 2809 | minSlack = slack; |
michael@0 | 2810 | minSlackEdge = id; |
michael@0 | 2811 | } |
michael@0 | 2812 | } |
michael@0 | 2813 | }); |
michael@0 | 2814 | } else { |
michael@0 | 2815 | graph.eachEdge(function(id, u, v, value) { |
michael@0 | 2816 | if (id !== e && !inSubtree(tree, u, lower) && inSubtree(tree, v, lower)) { |
michael@0 | 2817 | var slack = rankUtil.slack(graph, u, v, value.minLen); |
michael@0 | 2818 | if (slack < minSlack) { |
michael@0 | 2819 | minSlack = slack; |
michael@0 | 2820 | minSlackEdge = id; |
michael@0 | 2821 | } |
michael@0 | 2822 | } |
michael@0 | 2823 | }); |
michael@0 | 2824 | } |
michael@0 | 2825 | |
michael@0 | 2826 | if (minSlackEdge === undefined) { |
michael@0 | 2827 | var outside = []; |
michael@0 | 2828 | var inside = []; |
michael@0 | 2829 | graph.eachNode(function(id) { |
michael@0 | 2830 | if (!inSubtree(tree, id, lower)) { |
michael@0 | 2831 | outside.push(id); |
michael@0 | 2832 | } else { |
michael@0 | 2833 | inside.push(id); |
michael@0 | 2834 | } |
michael@0 | 2835 | }); |
michael@0 | 2836 | throw new Error('No edge found from outside of tree to inside'); |
michael@0 | 2837 | } |
michael@0 | 2838 | |
michael@0 | 2839 | return minSlackEdge; |
michael@0 | 2840 | } |
michael@0 | 2841 | |
michael@0 | 2842 | /* |
michael@0 | 2843 | * Replace edge e with edge f in the tree, recalculating the tree root, |
michael@0 | 2844 | * the nodes' low and lim properties and the edges' cut values. |
michael@0 | 2845 | */ |
michael@0 | 2846 | function exchange(graph, tree, e, f) { |
michael@0 | 2847 | tree.delEdge(e); |
michael@0 | 2848 | var source = graph.source(f); |
michael@0 | 2849 | var target = graph.target(f); |
michael@0 | 2850 | |
michael@0 | 2851 | // Redirect edges so that target is the root of its subtree. |
michael@0 | 2852 | function redirect(v) { |
michael@0 | 2853 | var edges = tree.inEdges(v); |
michael@0 | 2854 | for (var i in edges) { |
michael@0 | 2855 | var e = edges[i]; |
michael@0 | 2856 | var u = tree.source(e); |
michael@0 | 2857 | var value = tree.edge(e); |
michael@0 | 2858 | redirect(u); |
michael@0 | 2859 | tree.delEdge(e); |
michael@0 | 2860 | value.reversed = !value.reversed; |
michael@0 | 2861 | tree.addEdge(e, v, u, value); |
michael@0 | 2862 | } |
michael@0 | 2863 | } |
michael@0 | 2864 | |
michael@0 | 2865 | redirect(target); |
michael@0 | 2866 | |
michael@0 | 2867 | var root = source; |
michael@0 | 2868 | var edges = tree.inEdges(root); |
michael@0 | 2869 | while (edges.length > 0) { |
michael@0 | 2870 | root = tree.source(edges[0]); |
michael@0 | 2871 | edges = tree.inEdges(root); |
michael@0 | 2872 | } |
michael@0 | 2873 | |
michael@0 | 2874 | tree.graph().root = root; |
michael@0 | 2875 | |
michael@0 | 2876 | tree.addEdge(null, source, target, {cutValue: 0}); |
michael@0 | 2877 | |
michael@0 | 2878 | initCutValues(graph, tree); |
michael@0 | 2879 | |
michael@0 | 2880 | adjustRanks(graph, tree); |
michael@0 | 2881 | } |
michael@0 | 2882 | |
michael@0 | 2883 | /* |
michael@0 | 2884 | * Reset the ranks of all nodes based on the current spanning tree. |
michael@0 | 2885 | * The rank of the tree's root remains unchanged, while all other |
michael@0 | 2886 | * nodes are set to the sum of minimum length constraints along |
michael@0 | 2887 | * the path from the root. |
michael@0 | 2888 | */ |
michael@0 | 2889 | function adjustRanks(graph, tree) { |
michael@0 | 2890 | function dfs(p) { |
michael@0 | 2891 | var children = tree.successors(p); |
michael@0 | 2892 | children.forEach(function(c) { |
michael@0 | 2893 | var minLen = minimumLength(graph, p, c); |
michael@0 | 2894 | graph.node(c).rank = graph.node(p).rank + minLen; |
michael@0 | 2895 | dfs(c); |
michael@0 | 2896 | }); |
michael@0 | 2897 | } |
michael@0 | 2898 | |
michael@0 | 2899 | dfs(tree.graph().root); |
michael@0 | 2900 | } |
michael@0 | 2901 | |
michael@0 | 2902 | /* |
michael@0 | 2903 | * If u and v are connected by some edges in the graph, return the |
michael@0 | 2904 | * minimum length of those edges, as a positive number if v succeeds |
michael@0 | 2905 | * u and as a negative number if v precedes u. |
michael@0 | 2906 | */ |
michael@0 | 2907 | function minimumLength(graph, u, v) { |
michael@0 | 2908 | var outEdges = graph.outEdges(u, v); |
michael@0 | 2909 | if (outEdges.length > 0) { |
michael@0 | 2910 | return util.max(outEdges.map(function(e) { |
michael@0 | 2911 | return graph.edge(e).minLen; |
michael@0 | 2912 | })); |
michael@0 | 2913 | } |
michael@0 | 2914 | |
michael@0 | 2915 | var inEdges = graph.inEdges(u, v); |
michael@0 | 2916 | if (inEdges.length > 0) { |
michael@0 | 2917 | return -util.max(inEdges.map(function(e) { |
michael@0 | 2918 | return graph.edge(e).minLen; |
michael@0 | 2919 | })); |
michael@0 | 2920 | } |
michael@0 | 2921 | } |
michael@0 | 2922 | |
michael@0 | 2923 | },{"../util":26,"./rankUtil":24}],26:[function(require,module,exports){ |
michael@0 | 2924 | /* |
michael@0 | 2925 | * Returns the smallest value in the array. |
michael@0 | 2926 | */ |
michael@0 | 2927 | exports.min = function(values) { |
michael@0 | 2928 | return Math.min.apply(Math, values); |
michael@0 | 2929 | }; |
michael@0 | 2930 | |
michael@0 | 2931 | /* |
michael@0 | 2932 | * Returns the largest value in the array. |
michael@0 | 2933 | */ |
michael@0 | 2934 | exports.max = function(values) { |
michael@0 | 2935 | return Math.max.apply(Math, values); |
michael@0 | 2936 | }; |
michael@0 | 2937 | |
michael@0 | 2938 | /* |
michael@0 | 2939 | * Returns `true` only if `f(x)` is `true` for all `x` in `xs`. Otherwise |
michael@0 | 2940 | * returns `false`. This function will return immediately if it finds a |
michael@0 | 2941 | * case where `f(x)` does not hold. |
michael@0 | 2942 | */ |
michael@0 | 2943 | exports.all = function(xs, f) { |
michael@0 | 2944 | for (var i = 0; i < xs.length; ++i) { |
michael@0 | 2945 | if (!f(xs[i])) { |
michael@0 | 2946 | return false; |
michael@0 | 2947 | } |
michael@0 | 2948 | } |
michael@0 | 2949 | return true; |
michael@0 | 2950 | }; |
michael@0 | 2951 | |
michael@0 | 2952 | /* |
michael@0 | 2953 | * Accumulates the sum of elements in the given array using the `+` operator. |
michael@0 | 2954 | */ |
michael@0 | 2955 | exports.sum = function(values) { |
michael@0 | 2956 | return values.reduce(function(acc, x) { return acc + x; }, 0); |
michael@0 | 2957 | }; |
michael@0 | 2958 | |
michael@0 | 2959 | /* |
michael@0 | 2960 | * Returns an array of all values in the given object. |
michael@0 | 2961 | */ |
michael@0 | 2962 | exports.values = function(obj) { |
michael@0 | 2963 | return Object.keys(obj).map(function(k) { return obj[k]; }); |
michael@0 | 2964 | }; |
michael@0 | 2965 | |
michael@0 | 2966 | exports.shuffle = function(array) { |
michael@0 | 2967 | for (i = array.length - 1; i > 0; --i) { |
michael@0 | 2968 | var j = Math.floor(Math.random() * (i + 1)); |
michael@0 | 2969 | var aj = array[j]; |
michael@0 | 2970 | array[j] = array[i]; |
michael@0 | 2971 | array[i] = aj; |
michael@0 | 2972 | } |
michael@0 | 2973 | }; |
michael@0 | 2974 | |
michael@0 | 2975 | exports.propertyAccessor = function(self, config, field, setHook) { |
michael@0 | 2976 | return function(x) { |
michael@0 | 2977 | if (!arguments.length) return config[field]; |
michael@0 | 2978 | config[field] = x; |
michael@0 | 2979 | if (setHook) setHook(x); |
michael@0 | 2980 | return self; |
michael@0 | 2981 | }; |
michael@0 | 2982 | }; |
michael@0 | 2983 | |
michael@0 | 2984 | /* |
michael@0 | 2985 | * Given a layered, directed graph with `rank` and `order` node attributes, |
michael@0 | 2986 | * this function returns an array of ordered ranks. Each rank contains an array |
michael@0 | 2987 | * of the ids of the nodes in that rank in the order specified by the `order` |
michael@0 | 2988 | * attribute. |
michael@0 | 2989 | */ |
michael@0 | 2990 | exports.ordering = function(g) { |
michael@0 | 2991 | var ordering = []; |
michael@0 | 2992 | g.eachNode(function(u, value) { |
michael@0 | 2993 | var rank = ordering[value.rank] || (ordering[value.rank] = []); |
michael@0 | 2994 | rank[value.order] = u; |
michael@0 | 2995 | }); |
michael@0 | 2996 | return ordering; |
michael@0 | 2997 | }; |
michael@0 | 2998 | |
michael@0 | 2999 | /* |
michael@0 | 3000 | * A filter that can be used with `filterNodes` to get a graph that only |
michael@0 | 3001 | * includes nodes that do not contain others nodes. |
michael@0 | 3002 | */ |
michael@0 | 3003 | exports.filterNonSubgraphs = function(g) { |
michael@0 | 3004 | return function(u) { |
michael@0 | 3005 | return g.children(u).length === 0; |
michael@0 | 3006 | }; |
michael@0 | 3007 | }; |
michael@0 | 3008 | |
michael@0 | 3009 | /* |
michael@0 | 3010 | * Returns a new function that wraps `func` with a timer. The wrapper logs the |
michael@0 | 3011 | * time it takes to execute the function. |
michael@0 | 3012 | * |
michael@0 | 3013 | * The timer will be enabled provided `log.level >= 1`. |
michael@0 | 3014 | */ |
michael@0 | 3015 | function time(name, func) { |
michael@0 | 3016 | return function() { |
michael@0 | 3017 | var start = new Date().getTime(); |
michael@0 | 3018 | try { |
michael@0 | 3019 | return func.apply(null, arguments); |
michael@0 | 3020 | } finally { |
michael@0 | 3021 | log(1, name + ' time: ' + (new Date().getTime() - start) + 'ms'); |
michael@0 | 3022 | } |
michael@0 | 3023 | }; |
michael@0 | 3024 | } |
michael@0 | 3025 | time.enabled = false; |
michael@0 | 3026 | |
michael@0 | 3027 | exports.time = time; |
michael@0 | 3028 | |
michael@0 | 3029 | /* |
michael@0 | 3030 | * A global logger with the specification `log(level, message, ...)` that |
michael@0 | 3031 | * will log a message to the console if `log.level >= level`. |
michael@0 | 3032 | */ |
michael@0 | 3033 | function log(level) { |
michael@0 | 3034 | if (log.level >= level) { |
michael@0 | 3035 | console.log.apply(console, Array.prototype.slice.call(arguments, 1)); |
michael@0 | 3036 | } |
michael@0 | 3037 | } |
michael@0 | 3038 | log.level = 0; |
michael@0 | 3039 | |
michael@0 | 3040 | exports.log = log; |
michael@0 | 3041 | |
michael@0 | 3042 | },{}],27:[function(require,module,exports){ |
michael@0 | 3043 | module.exports = '0.4.5'; |
michael@0 | 3044 | |
michael@0 | 3045 | },{}],28:[function(require,module,exports){ |
michael@0 | 3046 | exports.Graph = require("./lib/Graph"); |
michael@0 | 3047 | exports.Digraph = require("./lib/Digraph"); |
michael@0 | 3048 | exports.CGraph = require("./lib/CGraph"); |
michael@0 | 3049 | exports.CDigraph = require("./lib/CDigraph"); |
michael@0 | 3050 | require("./lib/graph-converters"); |
michael@0 | 3051 | |
michael@0 | 3052 | exports.alg = { |
michael@0 | 3053 | isAcyclic: require("./lib/alg/isAcyclic"), |
michael@0 | 3054 | components: require("./lib/alg/components"), |
michael@0 | 3055 | dijkstra: require("./lib/alg/dijkstra"), |
michael@0 | 3056 | dijkstraAll: require("./lib/alg/dijkstraAll"), |
michael@0 | 3057 | findCycles: require("./lib/alg/findCycles"), |
michael@0 | 3058 | floydWarshall: require("./lib/alg/floydWarshall"), |
michael@0 | 3059 | postorder: require("./lib/alg/postorder"), |
michael@0 | 3060 | preorder: require("./lib/alg/preorder"), |
michael@0 | 3061 | prim: require("./lib/alg/prim"), |
michael@0 | 3062 | tarjan: require("./lib/alg/tarjan"), |
michael@0 | 3063 | topsort: require("./lib/alg/topsort") |
michael@0 | 3064 | }; |
michael@0 | 3065 | |
michael@0 | 3066 | exports.converter = { |
michael@0 | 3067 | json: require("./lib/converter/json.js") |
michael@0 | 3068 | }; |
michael@0 | 3069 | |
michael@0 | 3070 | var filter = require("./lib/filter"); |
michael@0 | 3071 | exports.filter = { |
michael@0 | 3072 | all: filter.all, |
michael@0 | 3073 | nodesFromList: filter.nodesFromList |
michael@0 | 3074 | }; |
michael@0 | 3075 | |
michael@0 | 3076 | exports.version = require("./lib/version"); |
michael@0 | 3077 | |
michael@0 | 3078 | },{"./lib/CDigraph":30,"./lib/CGraph":31,"./lib/Digraph":32,"./lib/Graph":33,"./lib/alg/components":34,"./lib/alg/dijkstra":35,"./lib/alg/dijkstraAll":36,"./lib/alg/findCycles":37,"./lib/alg/floydWarshall":38,"./lib/alg/isAcyclic":39,"./lib/alg/postorder":40,"./lib/alg/preorder":41,"./lib/alg/prim":42,"./lib/alg/tarjan":43,"./lib/alg/topsort":44,"./lib/converter/json.js":46,"./lib/filter":47,"./lib/graph-converters":48,"./lib/version":50}],29:[function(require,module,exports){ |
michael@0 | 3079 | /* jshint -W079 */ |
michael@0 | 3080 | var Set = require("cp-data").Set; |
michael@0 | 3081 | /* jshint +W079 */ |
michael@0 | 3082 | |
michael@0 | 3083 | module.exports = BaseGraph; |
michael@0 | 3084 | |
michael@0 | 3085 | function BaseGraph() { |
michael@0 | 3086 | // The value assigned to the graph itself. |
michael@0 | 3087 | this._value = undefined; |
michael@0 | 3088 | |
michael@0 | 3089 | // Map of node id -> { id, value } |
michael@0 | 3090 | this._nodes = {}; |
michael@0 | 3091 | |
michael@0 | 3092 | // Map of edge id -> { id, u, v, value } |
michael@0 | 3093 | this._edges = {}; |
michael@0 | 3094 | |
michael@0 | 3095 | // Used to generate a unique id in the graph |
michael@0 | 3096 | this._nextId = 0; |
michael@0 | 3097 | } |
michael@0 | 3098 | |
michael@0 | 3099 | // Number of nodes |
michael@0 | 3100 | BaseGraph.prototype.order = function() { |
michael@0 | 3101 | return Object.keys(this._nodes).length; |
michael@0 | 3102 | }; |
michael@0 | 3103 | |
michael@0 | 3104 | // Number of edges |
michael@0 | 3105 | BaseGraph.prototype.size = function() { |
michael@0 | 3106 | return Object.keys(this._edges).length; |
michael@0 | 3107 | }; |
michael@0 | 3108 | |
michael@0 | 3109 | // Accessor for graph level value |
michael@0 | 3110 | BaseGraph.prototype.graph = function(value) { |
michael@0 | 3111 | if (arguments.length === 0) { |
michael@0 | 3112 | return this._value; |
michael@0 | 3113 | } |
michael@0 | 3114 | this._value = value; |
michael@0 | 3115 | }; |
michael@0 | 3116 | |
michael@0 | 3117 | BaseGraph.prototype.hasNode = function(u) { |
michael@0 | 3118 | return u in this._nodes; |
michael@0 | 3119 | }; |
michael@0 | 3120 | |
michael@0 | 3121 | BaseGraph.prototype.node = function(u, value) { |
michael@0 | 3122 | var node = this._strictGetNode(u); |
michael@0 | 3123 | if (arguments.length === 1) { |
michael@0 | 3124 | return node.value; |
michael@0 | 3125 | } |
michael@0 | 3126 | node.value = value; |
michael@0 | 3127 | }; |
michael@0 | 3128 | |
michael@0 | 3129 | BaseGraph.prototype.nodes = function() { |
michael@0 | 3130 | var nodes = []; |
michael@0 | 3131 | this.eachNode(function(id) { nodes.push(id); }); |
michael@0 | 3132 | return nodes; |
michael@0 | 3133 | }; |
michael@0 | 3134 | |
michael@0 | 3135 | BaseGraph.prototype.eachNode = function(func) { |
michael@0 | 3136 | for (var k in this._nodes) { |
michael@0 | 3137 | var node = this._nodes[k]; |
michael@0 | 3138 | func(node.id, node.value); |
michael@0 | 3139 | } |
michael@0 | 3140 | }; |
michael@0 | 3141 | |
michael@0 | 3142 | BaseGraph.prototype.hasEdge = function(e) { |
michael@0 | 3143 | return e in this._edges; |
michael@0 | 3144 | }; |
michael@0 | 3145 | |
michael@0 | 3146 | BaseGraph.prototype.edge = function(e, value) { |
michael@0 | 3147 | var edge = this._strictGetEdge(e); |
michael@0 | 3148 | if (arguments.length === 1) { |
michael@0 | 3149 | return edge.value; |
michael@0 | 3150 | } |
michael@0 | 3151 | edge.value = value; |
michael@0 | 3152 | }; |
michael@0 | 3153 | |
michael@0 | 3154 | BaseGraph.prototype.edges = function() { |
michael@0 | 3155 | var es = []; |
michael@0 | 3156 | this.eachEdge(function(id) { es.push(id); }); |
michael@0 | 3157 | return es; |
michael@0 | 3158 | }; |
michael@0 | 3159 | |
michael@0 | 3160 | BaseGraph.prototype.eachEdge = function(func) { |
michael@0 | 3161 | for (var k in this._edges) { |
michael@0 | 3162 | var edge = this._edges[k]; |
michael@0 | 3163 | func(edge.id, edge.u, edge.v, edge.value); |
michael@0 | 3164 | } |
michael@0 | 3165 | }; |
michael@0 | 3166 | |
michael@0 | 3167 | BaseGraph.prototype.incidentNodes = function(e) { |
michael@0 | 3168 | var edge = this._strictGetEdge(e); |
michael@0 | 3169 | return [edge.u, edge.v]; |
michael@0 | 3170 | }; |
michael@0 | 3171 | |
michael@0 | 3172 | BaseGraph.prototype.addNode = function(u, value) { |
michael@0 | 3173 | if (u === undefined || u === null) { |
michael@0 | 3174 | do { |
michael@0 | 3175 | u = "_" + (++this._nextId); |
michael@0 | 3176 | } while (this.hasNode(u)); |
michael@0 | 3177 | } else if (this.hasNode(u)) { |
michael@0 | 3178 | throw new Error("Graph already has node '" + u + "'"); |
michael@0 | 3179 | } |
michael@0 | 3180 | this._nodes[u] = { id: u, value: value }; |
michael@0 | 3181 | return u; |
michael@0 | 3182 | }; |
michael@0 | 3183 | |
michael@0 | 3184 | BaseGraph.prototype.delNode = function(u) { |
michael@0 | 3185 | this._strictGetNode(u); |
michael@0 | 3186 | this.incidentEdges(u).forEach(function(e) { this.delEdge(e); }, this); |
michael@0 | 3187 | delete this._nodes[u]; |
michael@0 | 3188 | }; |
michael@0 | 3189 | |
michael@0 | 3190 | // inMap and outMap are opposite sides of an incidence map. For example, for |
michael@0 | 3191 | // Graph these would both come from the _incidentEdges map, while for Digraph |
michael@0 | 3192 | // they would come from _inEdges and _outEdges. |
michael@0 | 3193 | BaseGraph.prototype._addEdge = function(e, u, v, value, inMap, outMap) { |
michael@0 | 3194 | this._strictGetNode(u); |
michael@0 | 3195 | this._strictGetNode(v); |
michael@0 | 3196 | |
michael@0 | 3197 | if (e === undefined || e === null) { |
michael@0 | 3198 | do { |
michael@0 | 3199 | e = "_" + (++this._nextId); |
michael@0 | 3200 | } while (this.hasEdge(e)); |
michael@0 | 3201 | } |
michael@0 | 3202 | else if (this.hasEdge(e)) { |
michael@0 | 3203 | throw new Error("Graph already has edge '" + e + "'"); |
michael@0 | 3204 | } |
michael@0 | 3205 | |
michael@0 | 3206 | this._edges[e] = { id: e, u: u, v: v, value: value }; |
michael@0 | 3207 | addEdgeToMap(inMap[v], u, e); |
michael@0 | 3208 | addEdgeToMap(outMap[u], v, e); |
michael@0 | 3209 | |
michael@0 | 3210 | return e; |
michael@0 | 3211 | }; |
michael@0 | 3212 | |
michael@0 | 3213 | // See note for _addEdge regarding inMap and outMap. |
michael@0 | 3214 | BaseGraph.prototype._delEdge = function(e, inMap, outMap) { |
michael@0 | 3215 | var edge = this._strictGetEdge(e); |
michael@0 | 3216 | delEdgeFromMap(inMap[edge.v], edge.u, e); |
michael@0 | 3217 | delEdgeFromMap(outMap[edge.u], edge.v, e); |
michael@0 | 3218 | delete this._edges[e]; |
michael@0 | 3219 | }; |
michael@0 | 3220 | |
michael@0 | 3221 | BaseGraph.prototype.copy = function() { |
michael@0 | 3222 | var copy = new this.constructor(); |
michael@0 | 3223 | copy.graph(this.graph()); |
michael@0 | 3224 | this.eachNode(function(u, value) { copy.addNode(u, value); }); |
michael@0 | 3225 | this.eachEdge(function(e, u, v, value) { copy.addEdge(e, u, v, value); }); |
michael@0 | 3226 | copy._nextId = this._nextId; |
michael@0 | 3227 | return copy; |
michael@0 | 3228 | }; |
michael@0 | 3229 | |
michael@0 | 3230 | BaseGraph.prototype.filterNodes = function(filter) { |
michael@0 | 3231 | var copy = new this.constructor(); |
michael@0 | 3232 | copy.graph(this.graph()); |
michael@0 | 3233 | this.eachNode(function(u, value) { |
michael@0 | 3234 | if (filter(u)) { |
michael@0 | 3235 | copy.addNode(u, value); |
michael@0 | 3236 | } |
michael@0 | 3237 | }); |
michael@0 | 3238 | this.eachEdge(function(e, u, v, value) { |
michael@0 | 3239 | if (copy.hasNode(u) && copy.hasNode(v)) { |
michael@0 | 3240 | copy.addEdge(e, u, v, value); |
michael@0 | 3241 | } |
michael@0 | 3242 | }); |
michael@0 | 3243 | return copy; |
michael@0 | 3244 | }; |
michael@0 | 3245 | |
michael@0 | 3246 | BaseGraph.prototype._strictGetNode = function(u) { |
michael@0 | 3247 | var node = this._nodes[u]; |
michael@0 | 3248 | if (node === undefined) { |
michael@0 | 3249 | throw new Error("Node '" + u + "' is not in graph"); |
michael@0 | 3250 | } |
michael@0 | 3251 | return node; |
michael@0 | 3252 | }; |
michael@0 | 3253 | |
michael@0 | 3254 | BaseGraph.prototype._strictGetEdge = function(e) { |
michael@0 | 3255 | var edge = this._edges[e]; |
michael@0 | 3256 | if (edge === undefined) { |
michael@0 | 3257 | throw new Error("Edge '" + e + "' is not in graph"); |
michael@0 | 3258 | } |
michael@0 | 3259 | return edge; |
michael@0 | 3260 | }; |
michael@0 | 3261 | |
michael@0 | 3262 | function addEdgeToMap(map, v, e) { |
michael@0 | 3263 | (map[v] || (map[v] = new Set())).add(e); |
michael@0 | 3264 | } |
michael@0 | 3265 | |
michael@0 | 3266 | function delEdgeFromMap(map, v, e) { |
michael@0 | 3267 | var vEntry = map[v]; |
michael@0 | 3268 | vEntry.remove(e); |
michael@0 | 3269 | if (vEntry.size() === 0) { |
michael@0 | 3270 | delete map[v]; |
michael@0 | 3271 | } |
michael@0 | 3272 | } |
michael@0 | 3273 | |
michael@0 | 3274 | |
michael@0 | 3275 | },{"cp-data":5}],30:[function(require,module,exports){ |
michael@0 | 3276 | var Digraph = require("./Digraph"), |
michael@0 | 3277 | compoundify = require("./compoundify"); |
michael@0 | 3278 | |
michael@0 | 3279 | var CDigraph = compoundify(Digraph); |
michael@0 | 3280 | |
michael@0 | 3281 | module.exports = CDigraph; |
michael@0 | 3282 | |
michael@0 | 3283 | CDigraph.fromDigraph = function(src) { |
michael@0 | 3284 | var g = new CDigraph(), |
michael@0 | 3285 | graphValue = src.graph(); |
michael@0 | 3286 | |
michael@0 | 3287 | if (graphValue !== undefined) { |
michael@0 | 3288 | g.graph(graphValue); |
michael@0 | 3289 | } |
michael@0 | 3290 | |
michael@0 | 3291 | src.eachNode(function(u, value) { |
michael@0 | 3292 | if (value === undefined) { |
michael@0 | 3293 | g.addNode(u); |
michael@0 | 3294 | } else { |
michael@0 | 3295 | g.addNode(u, value); |
michael@0 | 3296 | } |
michael@0 | 3297 | }); |
michael@0 | 3298 | src.eachEdge(function(e, u, v, value) { |
michael@0 | 3299 | if (value === undefined) { |
michael@0 | 3300 | g.addEdge(null, u, v); |
michael@0 | 3301 | } else { |
michael@0 | 3302 | g.addEdge(null, u, v, value); |
michael@0 | 3303 | } |
michael@0 | 3304 | }); |
michael@0 | 3305 | return g; |
michael@0 | 3306 | }; |
michael@0 | 3307 | |
michael@0 | 3308 | CDigraph.prototype.toString = function() { |
michael@0 | 3309 | return "CDigraph " + JSON.stringify(this, null, 2); |
michael@0 | 3310 | }; |
michael@0 | 3311 | |
michael@0 | 3312 | },{"./Digraph":32,"./compoundify":45}],31:[function(require,module,exports){ |
michael@0 | 3313 | var Graph = require("./Graph"), |
michael@0 | 3314 | compoundify = require("./compoundify"); |
michael@0 | 3315 | |
michael@0 | 3316 | var CGraph = compoundify(Graph); |
michael@0 | 3317 | |
michael@0 | 3318 | module.exports = CGraph; |
michael@0 | 3319 | |
michael@0 | 3320 | CGraph.fromGraph = function(src) { |
michael@0 | 3321 | var g = new CGraph(), |
michael@0 | 3322 | graphValue = src.graph(); |
michael@0 | 3323 | |
michael@0 | 3324 | if (graphValue !== undefined) { |
michael@0 | 3325 | g.graph(graphValue); |
michael@0 | 3326 | } |
michael@0 | 3327 | |
michael@0 | 3328 | src.eachNode(function(u, value) { |
michael@0 | 3329 | if (value === undefined) { |
michael@0 | 3330 | g.addNode(u); |
michael@0 | 3331 | } else { |
michael@0 | 3332 | g.addNode(u, value); |
michael@0 | 3333 | } |
michael@0 | 3334 | }); |
michael@0 | 3335 | src.eachEdge(function(e, u, v, value) { |
michael@0 | 3336 | if (value === undefined) { |
michael@0 | 3337 | g.addEdge(null, u, v); |
michael@0 | 3338 | } else { |
michael@0 | 3339 | g.addEdge(null, u, v, value); |
michael@0 | 3340 | } |
michael@0 | 3341 | }); |
michael@0 | 3342 | return g; |
michael@0 | 3343 | }; |
michael@0 | 3344 | |
michael@0 | 3345 | CGraph.prototype.toString = function() { |
michael@0 | 3346 | return "CGraph " + JSON.stringify(this, null, 2); |
michael@0 | 3347 | }; |
michael@0 | 3348 | |
michael@0 | 3349 | },{"./Graph":33,"./compoundify":45}],32:[function(require,module,exports){ |
michael@0 | 3350 | /* |
michael@0 | 3351 | * This file is organized with in the following order: |
michael@0 | 3352 | * |
michael@0 | 3353 | * Exports |
michael@0 | 3354 | * Graph constructors |
michael@0 | 3355 | * Graph queries (e.g. nodes(), edges() |
michael@0 | 3356 | * Graph mutators |
michael@0 | 3357 | * Helper functions |
michael@0 | 3358 | */ |
michael@0 | 3359 | |
michael@0 | 3360 | var util = require("./util"), |
michael@0 | 3361 | BaseGraph = require("./BaseGraph"), |
michael@0 | 3362 | /* jshint -W079 */ |
michael@0 | 3363 | Set = require("cp-data").Set; |
michael@0 | 3364 | /* jshint +W079 */ |
michael@0 | 3365 | |
michael@0 | 3366 | module.exports = Digraph; |
michael@0 | 3367 | |
michael@0 | 3368 | /* |
michael@0 | 3369 | * Constructor to create a new directed multi-graph. |
michael@0 | 3370 | */ |
michael@0 | 3371 | function Digraph() { |
michael@0 | 3372 | BaseGraph.call(this); |
michael@0 | 3373 | |
michael@0 | 3374 | /*! Map of sourceId -> {targetId -> Set of edge ids} */ |
michael@0 | 3375 | this._inEdges = {}; |
michael@0 | 3376 | |
michael@0 | 3377 | /*! Map of targetId -> {sourceId -> Set of edge ids} */ |
michael@0 | 3378 | this._outEdges = {}; |
michael@0 | 3379 | } |
michael@0 | 3380 | |
michael@0 | 3381 | Digraph.prototype = new BaseGraph(); |
michael@0 | 3382 | Digraph.prototype.constructor = Digraph; |
michael@0 | 3383 | |
michael@0 | 3384 | /* |
michael@0 | 3385 | * Always returns `true`. |
michael@0 | 3386 | */ |
michael@0 | 3387 | Digraph.prototype.isDirected = function() { |
michael@0 | 3388 | return true; |
michael@0 | 3389 | }; |
michael@0 | 3390 | |
michael@0 | 3391 | /* |
michael@0 | 3392 | * Returns all successors of the node with the id `u`. That is, all nodes |
michael@0 | 3393 | * that have the node `u` as their source are returned. |
michael@0 | 3394 | * |
michael@0 | 3395 | * If no node `u` exists in the graph this function throws an Error. |
michael@0 | 3396 | * |
michael@0 | 3397 | * @param {String} u a node id |
michael@0 | 3398 | */ |
michael@0 | 3399 | Digraph.prototype.successors = function(u) { |
michael@0 | 3400 | this._strictGetNode(u); |
michael@0 | 3401 | return Object.keys(this._outEdges[u]) |
michael@0 | 3402 | .map(function(v) { return this._nodes[v].id; }, this); |
michael@0 | 3403 | }; |
michael@0 | 3404 | |
michael@0 | 3405 | /* |
michael@0 | 3406 | * Returns all predecessors of the node with the id `u`. That is, all nodes |
michael@0 | 3407 | * that have the node `u` as their target are returned. |
michael@0 | 3408 | * |
michael@0 | 3409 | * If no node `u` exists in the graph this function throws an Error. |
michael@0 | 3410 | * |
michael@0 | 3411 | * @param {String} u a node id |
michael@0 | 3412 | */ |
michael@0 | 3413 | Digraph.prototype.predecessors = function(u) { |
michael@0 | 3414 | this._strictGetNode(u); |
michael@0 | 3415 | return Object.keys(this._inEdges[u]) |
michael@0 | 3416 | .map(function(v) { return this._nodes[v].id; }, this); |
michael@0 | 3417 | }; |
michael@0 | 3418 | |
michael@0 | 3419 | /* |
michael@0 | 3420 | * Returns all nodes that are adjacent to the node with the id `u`. In other |
michael@0 | 3421 | * words, this function returns the set of all successors and predecessors of |
michael@0 | 3422 | * node `u`. |
michael@0 | 3423 | * |
michael@0 | 3424 | * @param {String} u a node id |
michael@0 | 3425 | */ |
michael@0 | 3426 | Digraph.prototype.neighbors = function(u) { |
michael@0 | 3427 | return Set.union([this.successors(u), this.predecessors(u)]).keys(); |
michael@0 | 3428 | }; |
michael@0 | 3429 | |
michael@0 | 3430 | /* |
michael@0 | 3431 | * Returns all nodes in the graph that have no in-edges. |
michael@0 | 3432 | */ |
michael@0 | 3433 | Digraph.prototype.sources = function() { |
michael@0 | 3434 | var self = this; |
michael@0 | 3435 | return this._filterNodes(function(u) { |
michael@0 | 3436 | // This could have better space characteristics if we had an inDegree function. |
michael@0 | 3437 | return self.inEdges(u).length === 0; |
michael@0 | 3438 | }); |
michael@0 | 3439 | }; |
michael@0 | 3440 | |
michael@0 | 3441 | /* |
michael@0 | 3442 | * Returns all nodes in the graph that have no out-edges. |
michael@0 | 3443 | */ |
michael@0 | 3444 | Digraph.prototype.sinks = function() { |
michael@0 | 3445 | var self = this; |
michael@0 | 3446 | return this._filterNodes(function(u) { |
michael@0 | 3447 | // This could have better space characteristics if we have an outDegree function. |
michael@0 | 3448 | return self.outEdges(u).length === 0; |
michael@0 | 3449 | }); |
michael@0 | 3450 | }; |
michael@0 | 3451 | |
michael@0 | 3452 | /* |
michael@0 | 3453 | * Returns the source node incident on the edge identified by the id `e`. If no |
michael@0 | 3454 | * such edge exists in the graph this function throws an Error. |
michael@0 | 3455 | * |
michael@0 | 3456 | * @param {String} e an edge id |
michael@0 | 3457 | */ |
michael@0 | 3458 | Digraph.prototype.source = function(e) { |
michael@0 | 3459 | return this._strictGetEdge(e).u; |
michael@0 | 3460 | }; |
michael@0 | 3461 | |
michael@0 | 3462 | /* |
michael@0 | 3463 | * Returns the target node incident on the edge identified by the id `e`. If no |
michael@0 | 3464 | * such edge exists in the graph this function throws an Error. |
michael@0 | 3465 | * |
michael@0 | 3466 | * @param {String} e an edge id |
michael@0 | 3467 | */ |
michael@0 | 3468 | Digraph.prototype.target = function(e) { |
michael@0 | 3469 | return this._strictGetEdge(e).v; |
michael@0 | 3470 | }; |
michael@0 | 3471 | |
michael@0 | 3472 | /* |
michael@0 | 3473 | * Returns an array of ids for all edges in the graph that have the node |
michael@0 | 3474 | * `target` as their target. If the node `target` is not in the graph this |
michael@0 | 3475 | * function raises an Error. |
michael@0 | 3476 | * |
michael@0 | 3477 | * Optionally a `source` node can also be specified. This causes the results |
michael@0 | 3478 | * to be filtered such that only edges from `source` to `target` are included. |
michael@0 | 3479 | * If the node `source` is specified but is not in the graph then this function |
michael@0 | 3480 | * raises an Error. |
michael@0 | 3481 | * |
michael@0 | 3482 | * @param {String} target the target node id |
michael@0 | 3483 | * @param {String} [source] an optional source node id |
michael@0 | 3484 | */ |
michael@0 | 3485 | Digraph.prototype.inEdges = function(target, source) { |
michael@0 | 3486 | this._strictGetNode(target); |
michael@0 | 3487 | var results = Set.union(util.values(this._inEdges[target])).keys(); |
michael@0 | 3488 | if (arguments.length > 1) { |
michael@0 | 3489 | this._strictGetNode(source); |
michael@0 | 3490 | results = results.filter(function(e) { return this.source(e) === source; }, this); |
michael@0 | 3491 | } |
michael@0 | 3492 | return results; |
michael@0 | 3493 | }; |
michael@0 | 3494 | |
michael@0 | 3495 | /* |
michael@0 | 3496 | * Returns an array of ids for all edges in the graph that have the node |
michael@0 | 3497 | * `source` as their source. If the node `source` is not in the graph this |
michael@0 | 3498 | * function raises an Error. |
michael@0 | 3499 | * |
michael@0 | 3500 | * Optionally a `target` node may also be specified. This causes the results |
michael@0 | 3501 | * to be filtered such that only edges from `source` to `target` are included. |
michael@0 | 3502 | * If the node `target` is specified but is not in the graph then this function |
michael@0 | 3503 | * raises an Error. |
michael@0 | 3504 | * |
michael@0 | 3505 | * @param {String} source the source node id |
michael@0 | 3506 | * @param {String} [target] an optional target node id |
michael@0 | 3507 | */ |
michael@0 | 3508 | Digraph.prototype.outEdges = function(source, target) { |
michael@0 | 3509 | this._strictGetNode(source); |
michael@0 | 3510 | var results = Set.union(util.values(this._outEdges[source])).keys(); |
michael@0 | 3511 | if (arguments.length > 1) { |
michael@0 | 3512 | this._strictGetNode(target); |
michael@0 | 3513 | results = results.filter(function(e) { return this.target(e) === target; }, this); |
michael@0 | 3514 | } |
michael@0 | 3515 | return results; |
michael@0 | 3516 | }; |
michael@0 | 3517 | |
michael@0 | 3518 | /* |
michael@0 | 3519 | * Returns an array of ids for all edges in the graph that have the `u` as |
michael@0 | 3520 | * their source or their target. If the node `u` is not in the graph this |
michael@0 | 3521 | * function raises an Error. |
michael@0 | 3522 | * |
michael@0 | 3523 | * Optionally a `v` node may also be specified. This causes the results to be |
michael@0 | 3524 | * filtered such that only edges between `u` and `v` - in either direction - |
michael@0 | 3525 | * are included. IF the node `v` is specified but not in the graph then this |
michael@0 | 3526 | * function raises an Error. |
michael@0 | 3527 | * |
michael@0 | 3528 | * @param {String} u the node for which to find incident edges |
michael@0 | 3529 | * @param {String} [v] option node that must be adjacent to `u` |
michael@0 | 3530 | */ |
michael@0 | 3531 | Digraph.prototype.incidentEdges = function(u, v) { |
michael@0 | 3532 | if (arguments.length > 1) { |
michael@0 | 3533 | return Set.union([this.outEdges(u, v), this.outEdges(v, u)]).keys(); |
michael@0 | 3534 | } else { |
michael@0 | 3535 | return Set.union([this.inEdges(u), this.outEdges(u)]).keys(); |
michael@0 | 3536 | } |
michael@0 | 3537 | }; |
michael@0 | 3538 | |
michael@0 | 3539 | /* |
michael@0 | 3540 | * Returns a string representation of this graph. |
michael@0 | 3541 | */ |
michael@0 | 3542 | Digraph.prototype.toString = function() { |
michael@0 | 3543 | return "Digraph " + JSON.stringify(this, null, 2); |
michael@0 | 3544 | }; |
michael@0 | 3545 | |
michael@0 | 3546 | /* |
michael@0 | 3547 | * Adds a new node with the id `u` to the graph and assigns it the value |
michael@0 | 3548 | * `value`. If a node with the id is already a part of the graph this function |
michael@0 | 3549 | * throws an Error. |
michael@0 | 3550 | * |
michael@0 | 3551 | * @param {String} u a node id |
michael@0 | 3552 | * @param {Object} [value] an optional value to attach to the node |
michael@0 | 3553 | */ |
michael@0 | 3554 | Digraph.prototype.addNode = function(u, value) { |
michael@0 | 3555 | u = BaseGraph.prototype.addNode.call(this, u, value); |
michael@0 | 3556 | this._inEdges[u] = {}; |
michael@0 | 3557 | this._outEdges[u] = {}; |
michael@0 | 3558 | return u; |
michael@0 | 3559 | }; |
michael@0 | 3560 | |
michael@0 | 3561 | /* |
michael@0 | 3562 | * Removes a node from the graph that has the id `u`. Any edges incident on the |
michael@0 | 3563 | * node are also removed. If the graph does not contain a node with the id this |
michael@0 | 3564 | * function will throw an Error. |
michael@0 | 3565 | * |
michael@0 | 3566 | * @param {String} u a node id |
michael@0 | 3567 | */ |
michael@0 | 3568 | Digraph.prototype.delNode = function(u) { |
michael@0 | 3569 | BaseGraph.prototype.delNode.call(this, u); |
michael@0 | 3570 | delete this._inEdges[u]; |
michael@0 | 3571 | delete this._outEdges[u]; |
michael@0 | 3572 | }; |
michael@0 | 3573 | |
michael@0 | 3574 | /* |
michael@0 | 3575 | * Adds a new edge to the graph with the id `e` from a node with the id `source` |
michael@0 | 3576 | * to a node with an id `target` and assigns it the value `value`. This graph |
michael@0 | 3577 | * allows more than one edge from `source` to `target` as long as the id `e` |
michael@0 | 3578 | * is unique in the set of edges. If `e` is `null` the graph will assign a |
michael@0 | 3579 | * unique identifier to the edge. |
michael@0 | 3580 | * |
michael@0 | 3581 | * If `source` or `target` are not present in the graph this function will |
michael@0 | 3582 | * throw an Error. |
michael@0 | 3583 | * |
michael@0 | 3584 | * @param {String} [e] an edge id |
michael@0 | 3585 | * @param {String} source the source node id |
michael@0 | 3586 | * @param {String} target the target node id |
michael@0 | 3587 | * @param {Object} [value] an optional value to attach to the edge |
michael@0 | 3588 | */ |
michael@0 | 3589 | Digraph.prototype.addEdge = function(e, source, target, value) { |
michael@0 | 3590 | return BaseGraph.prototype._addEdge.call(this, e, source, target, value, |
michael@0 | 3591 | this._inEdges, this._outEdges); |
michael@0 | 3592 | }; |
michael@0 | 3593 | |
michael@0 | 3594 | /* |
michael@0 | 3595 | * Removes an edge in the graph with the id `e`. If no edge in the graph has |
michael@0 | 3596 | * the id `e` this function will throw an Error. |
michael@0 | 3597 | * |
michael@0 | 3598 | * @param {String} e an edge id |
michael@0 | 3599 | */ |
michael@0 | 3600 | Digraph.prototype.delEdge = function(e) { |
michael@0 | 3601 | BaseGraph.prototype._delEdge.call(this, e, this._inEdges, this._outEdges); |
michael@0 | 3602 | }; |
michael@0 | 3603 | |
michael@0 | 3604 | // Unlike BaseGraph.filterNodes, this helper just returns nodes that |
michael@0 | 3605 | // satisfy a predicate. |
michael@0 | 3606 | Digraph.prototype._filterNodes = function(pred) { |
michael@0 | 3607 | var filtered = []; |
michael@0 | 3608 | this.eachNode(function(u) { |
michael@0 | 3609 | if (pred(u)) { |
michael@0 | 3610 | filtered.push(u); |
michael@0 | 3611 | } |
michael@0 | 3612 | }); |
michael@0 | 3613 | return filtered; |
michael@0 | 3614 | }; |
michael@0 | 3615 | |
michael@0 | 3616 | |
michael@0 | 3617 | },{"./BaseGraph":29,"./util":49,"cp-data":5}],33:[function(require,module,exports){ |
michael@0 | 3618 | /* |
michael@0 | 3619 | * This file is organized with in the following order: |
michael@0 | 3620 | * |
michael@0 | 3621 | * Exports |
michael@0 | 3622 | * Graph constructors |
michael@0 | 3623 | * Graph queries (e.g. nodes(), edges() |
michael@0 | 3624 | * Graph mutators |
michael@0 | 3625 | * Helper functions |
michael@0 | 3626 | */ |
michael@0 | 3627 | |
michael@0 | 3628 | var util = require("./util"), |
michael@0 | 3629 | BaseGraph = require("./BaseGraph"), |
michael@0 | 3630 | /* jshint -W079 */ |
michael@0 | 3631 | Set = require("cp-data").Set; |
michael@0 | 3632 | /* jshint +W079 */ |
michael@0 | 3633 | |
michael@0 | 3634 | module.exports = Graph; |
michael@0 | 3635 | |
michael@0 | 3636 | /* |
michael@0 | 3637 | * Constructor to create a new undirected multi-graph. |
michael@0 | 3638 | */ |
michael@0 | 3639 | function Graph() { |
michael@0 | 3640 | BaseGraph.call(this); |
michael@0 | 3641 | |
michael@0 | 3642 | /*! Map of nodeId -> { otherNodeId -> Set of edge ids } */ |
michael@0 | 3643 | this._incidentEdges = {}; |
michael@0 | 3644 | } |
michael@0 | 3645 | |
michael@0 | 3646 | Graph.prototype = new BaseGraph(); |
michael@0 | 3647 | Graph.prototype.constructor = Graph; |
michael@0 | 3648 | |
michael@0 | 3649 | /* |
michael@0 | 3650 | * Always returns `false`. |
michael@0 | 3651 | */ |
michael@0 | 3652 | Graph.prototype.isDirected = function() { |
michael@0 | 3653 | return false; |
michael@0 | 3654 | }; |
michael@0 | 3655 | |
michael@0 | 3656 | /* |
michael@0 | 3657 | * Returns all nodes that are adjacent to the node with the id `u`. |
michael@0 | 3658 | * |
michael@0 | 3659 | * @param {String} u a node id |
michael@0 | 3660 | */ |
michael@0 | 3661 | Graph.prototype.neighbors = function(u) { |
michael@0 | 3662 | this._strictGetNode(u); |
michael@0 | 3663 | return Object.keys(this._incidentEdges[u]) |
michael@0 | 3664 | .map(function(v) { return this._nodes[v].id; }, this); |
michael@0 | 3665 | }; |
michael@0 | 3666 | |
michael@0 | 3667 | /* |
michael@0 | 3668 | * Returns an array of ids for all edges in the graph that are incident on `u`. |
michael@0 | 3669 | * If the node `u` is not in the graph this function raises an Error. |
michael@0 | 3670 | * |
michael@0 | 3671 | * Optionally a `v` node may also be specified. This causes the results to be |
michael@0 | 3672 | * filtered such that only edges between `u` and `v` are included. If the node |
michael@0 | 3673 | * `v` is specified but not in the graph then this function raises an Error. |
michael@0 | 3674 | * |
michael@0 | 3675 | * @param {String} u the node for which to find incident edges |
michael@0 | 3676 | * @param {String} [v] option node that must be adjacent to `u` |
michael@0 | 3677 | */ |
michael@0 | 3678 | Graph.prototype.incidentEdges = function(u, v) { |
michael@0 | 3679 | this._strictGetNode(u); |
michael@0 | 3680 | if (arguments.length > 1) { |
michael@0 | 3681 | this._strictGetNode(v); |
michael@0 | 3682 | return v in this._incidentEdges[u] ? this._incidentEdges[u][v].keys() : []; |
michael@0 | 3683 | } else { |
michael@0 | 3684 | return Set.union(util.values(this._incidentEdges[u])).keys(); |
michael@0 | 3685 | } |
michael@0 | 3686 | }; |
michael@0 | 3687 | |
michael@0 | 3688 | /* |
michael@0 | 3689 | * Returns a string representation of this graph. |
michael@0 | 3690 | */ |
michael@0 | 3691 | Graph.prototype.toString = function() { |
michael@0 | 3692 | return "Graph " + JSON.stringify(this, null, 2); |
michael@0 | 3693 | }; |
michael@0 | 3694 | |
michael@0 | 3695 | /* |
michael@0 | 3696 | * Adds a new node with the id `u` to the graph and assigns it the value |
michael@0 | 3697 | * `value`. If a node with the id is already a part of the graph this function |
michael@0 | 3698 | * throws an Error. |
michael@0 | 3699 | * |
michael@0 | 3700 | * @param {String} u a node id |
michael@0 | 3701 | * @param {Object} [value] an optional value to attach to the node |
michael@0 | 3702 | */ |
michael@0 | 3703 | Graph.prototype.addNode = function(u, value) { |
michael@0 | 3704 | u = BaseGraph.prototype.addNode.call(this, u, value); |
michael@0 | 3705 | this._incidentEdges[u] = {}; |
michael@0 | 3706 | return u; |
michael@0 | 3707 | }; |
michael@0 | 3708 | |
michael@0 | 3709 | /* |
michael@0 | 3710 | * Removes a node from the graph that has the id `u`. Any edges incident on the |
michael@0 | 3711 | * node are also removed. If the graph does not contain a node with the id this |
michael@0 | 3712 | * function will throw an Error. |
michael@0 | 3713 | * |
michael@0 | 3714 | * @param {String} u a node id |
michael@0 | 3715 | */ |
michael@0 | 3716 | Graph.prototype.delNode = function(u) { |
michael@0 | 3717 | BaseGraph.prototype.delNode.call(this, u); |
michael@0 | 3718 | delete this._incidentEdges[u]; |
michael@0 | 3719 | }; |
michael@0 | 3720 | |
michael@0 | 3721 | /* |
michael@0 | 3722 | * Adds a new edge to the graph with the id `e` between a node with the id `u` |
michael@0 | 3723 | * and a node with an id `v` and assigns it the value `value`. This graph |
michael@0 | 3724 | * allows more than one edge between `u` and `v` as long as the id `e` |
michael@0 | 3725 | * is unique in the set of edges. If `e` is `null` the graph will assign a |
michael@0 | 3726 | * unique identifier to the edge. |
michael@0 | 3727 | * |
michael@0 | 3728 | * If `u` or `v` are not present in the graph this function will throw an |
michael@0 | 3729 | * Error. |
michael@0 | 3730 | * |
michael@0 | 3731 | * @param {String} [e] an edge id |
michael@0 | 3732 | * @param {String} u the node id of one of the adjacent nodes |
michael@0 | 3733 | * @param {String} v the node id of the other adjacent node |
michael@0 | 3734 | * @param {Object} [value] an optional value to attach to the edge |
michael@0 | 3735 | */ |
michael@0 | 3736 | Graph.prototype.addEdge = function(e, u, v, value) { |
michael@0 | 3737 | return BaseGraph.prototype._addEdge.call(this, e, u, v, value, |
michael@0 | 3738 | this._incidentEdges, this._incidentEdges); |
michael@0 | 3739 | }; |
michael@0 | 3740 | |
michael@0 | 3741 | /* |
michael@0 | 3742 | * Removes an edge in the graph with the id `e`. If no edge in the graph has |
michael@0 | 3743 | * the id `e` this function will throw an Error. |
michael@0 | 3744 | * |
michael@0 | 3745 | * @param {String} e an edge id |
michael@0 | 3746 | */ |
michael@0 | 3747 | Graph.prototype.delEdge = function(e) { |
michael@0 | 3748 | BaseGraph.prototype._delEdge.call(this, e, this._incidentEdges, this._incidentEdges); |
michael@0 | 3749 | }; |
michael@0 | 3750 | |
michael@0 | 3751 | |
michael@0 | 3752 | },{"./BaseGraph":29,"./util":49,"cp-data":5}],34:[function(require,module,exports){ |
michael@0 | 3753 | /* jshint -W079 */ |
michael@0 | 3754 | var Set = require("cp-data").Set; |
michael@0 | 3755 | /* jshint +W079 */ |
michael@0 | 3756 | |
michael@0 | 3757 | module.exports = components; |
michael@0 | 3758 | |
michael@0 | 3759 | /** |
michael@0 | 3760 | * Finds all [connected components][] in a graph and returns an array of these |
michael@0 | 3761 | * components. Each component is itself an array that contains the ids of nodes |
michael@0 | 3762 | * in the component. |
michael@0 | 3763 | * |
michael@0 | 3764 | * This function only works with undirected Graphs. |
michael@0 | 3765 | * |
michael@0 | 3766 | * [connected components]: http://en.wikipedia.org/wiki/Connected_component_(graph_theory) |
michael@0 | 3767 | * |
michael@0 | 3768 | * @param {Graph} g the graph to search for components |
michael@0 | 3769 | */ |
michael@0 | 3770 | function components(g) { |
michael@0 | 3771 | var results = []; |
michael@0 | 3772 | var visited = new Set(); |
michael@0 | 3773 | |
michael@0 | 3774 | function dfs(v, component) { |
michael@0 | 3775 | if (!visited.has(v)) { |
michael@0 | 3776 | visited.add(v); |
michael@0 | 3777 | component.push(v); |
michael@0 | 3778 | g.neighbors(v).forEach(function(w) { |
michael@0 | 3779 | dfs(w, component); |
michael@0 | 3780 | }); |
michael@0 | 3781 | } |
michael@0 | 3782 | } |
michael@0 | 3783 | |
michael@0 | 3784 | g.nodes().forEach(function(v) { |
michael@0 | 3785 | var component = []; |
michael@0 | 3786 | dfs(v, component); |
michael@0 | 3787 | if (component.length > 0) { |
michael@0 | 3788 | results.push(component); |
michael@0 | 3789 | } |
michael@0 | 3790 | }); |
michael@0 | 3791 | |
michael@0 | 3792 | return results; |
michael@0 | 3793 | } |
michael@0 | 3794 | |
michael@0 | 3795 | },{"cp-data":5}],35:[function(require,module,exports){ |
michael@0 | 3796 | var PriorityQueue = require("cp-data").PriorityQueue; |
michael@0 | 3797 | |
michael@0 | 3798 | module.exports = dijkstra; |
michael@0 | 3799 | |
michael@0 | 3800 | /** |
michael@0 | 3801 | * This function is an implementation of [Dijkstra's algorithm][] which finds |
michael@0 | 3802 | * the shortest path from **source** to all other nodes in **g**. This |
michael@0 | 3803 | * function returns a map of `u -> { distance, predecessor }`. The distance |
michael@0 | 3804 | * property holds the sum of the weights from **source** to `u` along the |
michael@0 | 3805 | * shortest path or `Number.POSITIVE_INFINITY` if there is no path from |
michael@0 | 3806 | * **source**. The predecessor property can be used to walk the individual |
michael@0 | 3807 | * elements of the path from **source** to **u** in reverse order. |
michael@0 | 3808 | * |
michael@0 | 3809 | * This function takes an optional `weightFunc(e)` which returns the |
michael@0 | 3810 | * weight of the edge `e`. If no weightFunc is supplied then each edge is |
michael@0 | 3811 | * assumed to have a weight of 1. This function throws an Error if any of |
michael@0 | 3812 | * the traversed edges have a negative edge weight. |
michael@0 | 3813 | * |
michael@0 | 3814 | * This function takes an optional `incidentFunc(u)` which returns the ids of |
michael@0 | 3815 | * all edges incident to the node `u` for the purposes of shortest path |
michael@0 | 3816 | * traversal. By default this function uses the `g.outEdges` for Digraphs and |
michael@0 | 3817 | * `g.incidentEdges` for Graphs. |
michael@0 | 3818 | * |
michael@0 | 3819 | * This function takes `O((|E| + |V|) * log |V|)` time. |
michael@0 | 3820 | * |
michael@0 | 3821 | * [Dijkstra's algorithm]: http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm |
michael@0 | 3822 | * |
michael@0 | 3823 | * @param {Graph} g the graph to search for shortest paths from **source** |
michael@0 | 3824 | * @param {Object} source the source from which to start the search |
michael@0 | 3825 | * @param {Function} [weightFunc] optional weight function |
michael@0 | 3826 | * @param {Function} [incidentFunc] optional incident function |
michael@0 | 3827 | */ |
michael@0 | 3828 | function dijkstra(g, source, weightFunc, incidentFunc) { |
michael@0 | 3829 | var results = {}, |
michael@0 | 3830 | pq = new PriorityQueue(); |
michael@0 | 3831 | |
michael@0 | 3832 | function updateNeighbors(e) { |
michael@0 | 3833 | var incidentNodes = g.incidentNodes(e), |
michael@0 | 3834 | v = incidentNodes[0] !== u ? incidentNodes[0] : incidentNodes[1], |
michael@0 | 3835 | vEntry = results[v], |
michael@0 | 3836 | weight = weightFunc(e), |
michael@0 | 3837 | distance = uEntry.distance + weight; |
michael@0 | 3838 | |
michael@0 | 3839 | if (weight < 0) { |
michael@0 | 3840 | throw new Error("dijkstra does not allow negative edge weights. Bad edge: " + e + " Weight: " + weight); |
michael@0 | 3841 | } |
michael@0 | 3842 | |
michael@0 | 3843 | if (distance < vEntry.distance) { |
michael@0 | 3844 | vEntry.distance = distance; |
michael@0 | 3845 | vEntry.predecessor = u; |
michael@0 | 3846 | pq.decrease(v, distance); |
michael@0 | 3847 | } |
michael@0 | 3848 | } |
michael@0 | 3849 | |
michael@0 | 3850 | weightFunc = weightFunc || function() { return 1; }; |
michael@0 | 3851 | incidentFunc = incidentFunc || (g.isDirected() |
michael@0 | 3852 | ? function(u) { return g.outEdges(u); } |
michael@0 | 3853 | : function(u) { return g.incidentEdges(u); }); |
michael@0 | 3854 | |
michael@0 | 3855 | g.eachNode(function(u) { |
michael@0 | 3856 | var distance = u === source ? 0 : Number.POSITIVE_INFINITY; |
michael@0 | 3857 | results[u] = { distance: distance }; |
michael@0 | 3858 | pq.add(u, distance); |
michael@0 | 3859 | }); |
michael@0 | 3860 | |
michael@0 | 3861 | var u, uEntry; |
michael@0 | 3862 | while (pq.size() > 0) { |
michael@0 | 3863 | u = pq.removeMin(); |
michael@0 | 3864 | uEntry = results[u]; |
michael@0 | 3865 | if (uEntry.distance === Number.POSITIVE_INFINITY) { |
michael@0 | 3866 | break; |
michael@0 | 3867 | } |
michael@0 | 3868 | |
michael@0 | 3869 | incidentFunc(u).forEach(updateNeighbors); |
michael@0 | 3870 | } |
michael@0 | 3871 | |
michael@0 | 3872 | return results; |
michael@0 | 3873 | } |
michael@0 | 3874 | |
michael@0 | 3875 | },{"cp-data":5}],36:[function(require,module,exports){ |
michael@0 | 3876 | var dijkstra = require("./dijkstra"); |
michael@0 | 3877 | |
michael@0 | 3878 | module.exports = dijkstraAll; |
michael@0 | 3879 | |
michael@0 | 3880 | /** |
michael@0 | 3881 | * This function finds the shortest path from each node to every other |
michael@0 | 3882 | * reachable node in the graph. It is similar to [alg.dijkstra][], but |
michael@0 | 3883 | * instead of returning a single-source array, it returns a mapping of |
michael@0 | 3884 | * of `source -> alg.dijksta(g, source, weightFunc, incidentFunc)`. |
michael@0 | 3885 | * |
michael@0 | 3886 | * This function takes an optional `weightFunc(e)` which returns the |
michael@0 | 3887 | * weight of the edge `e`. If no weightFunc is supplied then each edge is |
michael@0 | 3888 | * assumed to have a weight of 1. This function throws an Error if any of |
michael@0 | 3889 | * the traversed edges have a negative edge weight. |
michael@0 | 3890 | * |
michael@0 | 3891 | * This function takes an optional `incidentFunc(u)` which returns the ids of |
michael@0 | 3892 | * all edges incident to the node `u` for the purposes of shortest path |
michael@0 | 3893 | * traversal. By default this function uses the `outEdges` function on the |
michael@0 | 3894 | * supplied graph. |
michael@0 | 3895 | * |
michael@0 | 3896 | * This function takes `O(|V| * (|E| + |V|) * log |V|)` time. |
michael@0 | 3897 | * |
michael@0 | 3898 | * [alg.dijkstra]: dijkstra.js.html#dijkstra |
michael@0 | 3899 | * |
michael@0 | 3900 | * @param {Graph} g the graph to search for shortest paths from **source** |
michael@0 | 3901 | * @param {Function} [weightFunc] optional weight function |
michael@0 | 3902 | * @param {Function} [incidentFunc] optional incident function |
michael@0 | 3903 | */ |
michael@0 | 3904 | function dijkstraAll(g, weightFunc, incidentFunc) { |
michael@0 | 3905 | var results = {}; |
michael@0 | 3906 | g.eachNode(function(u) { |
michael@0 | 3907 | results[u] = dijkstra(g, u, weightFunc, incidentFunc); |
michael@0 | 3908 | }); |
michael@0 | 3909 | return results; |
michael@0 | 3910 | } |
michael@0 | 3911 | |
michael@0 | 3912 | },{"./dijkstra":35}],37:[function(require,module,exports){ |
michael@0 | 3913 | var tarjan = require("./tarjan"); |
michael@0 | 3914 | |
michael@0 | 3915 | module.exports = findCycles; |
michael@0 | 3916 | |
michael@0 | 3917 | /* |
michael@0 | 3918 | * Given a Digraph **g** this function returns all nodes that are part of a |
michael@0 | 3919 | * cycle. Since there may be more than one cycle in a graph this function |
michael@0 | 3920 | * returns an array of these cycles, where each cycle is itself represented |
michael@0 | 3921 | * by an array of ids for each node involved in that cycle. |
michael@0 | 3922 | * |
michael@0 | 3923 | * [alg.isAcyclic][] is more efficient if you only need to determine whether |
michael@0 | 3924 | * a graph has a cycle or not. |
michael@0 | 3925 | * |
michael@0 | 3926 | * [alg.isAcyclic]: isAcyclic.js.html#isAcyclic |
michael@0 | 3927 | * |
michael@0 | 3928 | * @param {Digraph} g the graph to search for cycles. |
michael@0 | 3929 | */ |
michael@0 | 3930 | function findCycles(g) { |
michael@0 | 3931 | return tarjan(g).filter(function(cmpt) { return cmpt.length > 1; }); |
michael@0 | 3932 | } |
michael@0 | 3933 | |
michael@0 | 3934 | },{"./tarjan":43}],38:[function(require,module,exports){ |
michael@0 | 3935 | module.exports = floydWarshall; |
michael@0 | 3936 | |
michael@0 | 3937 | /** |
michael@0 | 3938 | * This function is an implementation of the [Floyd-Warshall algorithm][], |
michael@0 | 3939 | * which finds the shortest path from each node to every other reachable node |
michael@0 | 3940 | * in the graph. It is similar to [alg.dijkstraAll][], but it handles negative |
michael@0 | 3941 | * edge weights and is more efficient for some types of graphs. This function |
michael@0 | 3942 | * returns a map of `source -> { target -> { distance, predecessor }`. The |
michael@0 | 3943 | * distance property holds the sum of the weights from `source` to `target` |
michael@0 | 3944 | * along the shortest path of `Number.POSITIVE_INFINITY` if there is no path |
michael@0 | 3945 | * from `source`. The predecessor property can be used to walk the individual |
michael@0 | 3946 | * elements of the path from `source` to `target` in reverse order. |
michael@0 | 3947 | * |
michael@0 | 3948 | * This function takes an optional `weightFunc(e)` which returns the |
michael@0 | 3949 | * weight of the edge `e`. If no weightFunc is supplied then each edge is |
michael@0 | 3950 | * assumed to have a weight of 1. |
michael@0 | 3951 | * |
michael@0 | 3952 | * This function takes an optional `incidentFunc(u)` which returns the ids of |
michael@0 | 3953 | * all edges incident to the node `u` for the purposes of shortest path |
michael@0 | 3954 | * traversal. By default this function uses the `outEdges` function on the |
michael@0 | 3955 | * supplied graph. |
michael@0 | 3956 | * |
michael@0 | 3957 | * This algorithm takes O(|V|^3) time. |
michael@0 | 3958 | * |
michael@0 | 3959 | * [Floyd-Warshall algorithm]: https://en.wikipedia.org/wiki/Floyd-Warshall_algorithm |
michael@0 | 3960 | * [alg.dijkstraAll]: dijkstraAll.js.html#dijkstraAll |
michael@0 | 3961 | * |
michael@0 | 3962 | * @param {Graph} g the graph to search for shortest paths from **source** |
michael@0 | 3963 | * @param {Function} [weightFunc] optional weight function |
michael@0 | 3964 | * @param {Function} [incidentFunc] optional incident function |
michael@0 | 3965 | */ |
michael@0 | 3966 | function floydWarshall(g, weightFunc, incidentFunc) { |
michael@0 | 3967 | var results = {}, |
michael@0 | 3968 | nodes = g.nodes(); |
michael@0 | 3969 | |
michael@0 | 3970 | weightFunc = weightFunc || function() { return 1; }; |
michael@0 | 3971 | incidentFunc = incidentFunc || (g.isDirected() |
michael@0 | 3972 | ? function(u) { return g.outEdges(u); } |
michael@0 | 3973 | : function(u) { return g.incidentEdges(u); }); |
michael@0 | 3974 | |
michael@0 | 3975 | nodes.forEach(function(u) { |
michael@0 | 3976 | results[u] = {}; |
michael@0 | 3977 | results[u][u] = { distance: 0 }; |
michael@0 | 3978 | nodes.forEach(function(v) { |
michael@0 | 3979 | if (u !== v) { |
michael@0 | 3980 | results[u][v] = { distance: Number.POSITIVE_INFINITY }; |
michael@0 | 3981 | } |
michael@0 | 3982 | }); |
michael@0 | 3983 | incidentFunc(u).forEach(function(e) { |
michael@0 | 3984 | var incidentNodes = g.incidentNodes(e), |
michael@0 | 3985 | v = incidentNodes[0] !== u ? incidentNodes[0] : incidentNodes[1], |
michael@0 | 3986 | d = weightFunc(e); |
michael@0 | 3987 | if (d < results[u][v].distance) { |
michael@0 | 3988 | results[u][v] = { distance: d, predecessor: u }; |
michael@0 | 3989 | } |
michael@0 | 3990 | }); |
michael@0 | 3991 | }); |
michael@0 | 3992 | |
michael@0 | 3993 | nodes.forEach(function(k) { |
michael@0 | 3994 | var rowK = results[k]; |
michael@0 | 3995 | nodes.forEach(function(i) { |
michael@0 | 3996 | var rowI = results[i]; |
michael@0 | 3997 | nodes.forEach(function(j) { |
michael@0 | 3998 | var ik = rowI[k]; |
michael@0 | 3999 | var kj = rowK[j]; |
michael@0 | 4000 | var ij = rowI[j]; |
michael@0 | 4001 | var altDistance = ik.distance + kj.distance; |
michael@0 | 4002 | if (altDistance < ij.distance) { |
michael@0 | 4003 | ij.distance = altDistance; |
michael@0 | 4004 | ij.predecessor = kj.predecessor; |
michael@0 | 4005 | } |
michael@0 | 4006 | }); |
michael@0 | 4007 | }); |
michael@0 | 4008 | }); |
michael@0 | 4009 | |
michael@0 | 4010 | return results; |
michael@0 | 4011 | } |
michael@0 | 4012 | |
michael@0 | 4013 | },{}],39:[function(require,module,exports){ |
michael@0 | 4014 | var topsort = require("./topsort"); |
michael@0 | 4015 | |
michael@0 | 4016 | module.exports = isAcyclic; |
michael@0 | 4017 | |
michael@0 | 4018 | /* |
michael@0 | 4019 | * Given a Digraph **g** this function returns `true` if the graph has no |
michael@0 | 4020 | * cycles and returns `false` if it does. This algorithm returns as soon as it |
michael@0 | 4021 | * detects the first cycle. |
michael@0 | 4022 | * |
michael@0 | 4023 | * Use [alg.findCycles][] if you need the actual list of cycles in a graph. |
michael@0 | 4024 | * |
michael@0 | 4025 | * [alg.findCycles]: findCycles.js.html#findCycles |
michael@0 | 4026 | * |
michael@0 | 4027 | * @param {Digraph} g the graph to test for cycles |
michael@0 | 4028 | */ |
michael@0 | 4029 | function isAcyclic(g) { |
michael@0 | 4030 | try { |
michael@0 | 4031 | topsort(g); |
michael@0 | 4032 | } catch (e) { |
michael@0 | 4033 | if (e instanceof topsort.CycleException) return false; |
michael@0 | 4034 | throw e; |
michael@0 | 4035 | } |
michael@0 | 4036 | return true; |
michael@0 | 4037 | } |
michael@0 | 4038 | |
michael@0 | 4039 | },{"./topsort":44}],40:[function(require,module,exports){ |
michael@0 | 4040 | /* jshint -W079 */ |
michael@0 | 4041 | var Set = require("cp-data").Set; |
michael@0 | 4042 | /* jshint +W079 */ |
michael@0 | 4043 | |
michael@0 | 4044 | module.exports = postorder; |
michael@0 | 4045 | |
michael@0 | 4046 | // Postorder traversal of g, calling f for each visited node. Assumes the graph |
michael@0 | 4047 | // is a tree. |
michael@0 | 4048 | function postorder(g, root, f) { |
michael@0 | 4049 | var visited = new Set(); |
michael@0 | 4050 | if (g.isDirected()) { |
michael@0 | 4051 | throw new Error("This function only works for undirected graphs"); |
michael@0 | 4052 | } |
michael@0 | 4053 | function dfs(u, prev) { |
michael@0 | 4054 | if (visited.has(u)) { |
michael@0 | 4055 | throw new Error("The input graph is not a tree: " + g); |
michael@0 | 4056 | } |
michael@0 | 4057 | visited.add(u); |
michael@0 | 4058 | g.neighbors(u).forEach(function(v) { |
michael@0 | 4059 | if (v !== prev) dfs(v, u); |
michael@0 | 4060 | }); |
michael@0 | 4061 | f(u); |
michael@0 | 4062 | } |
michael@0 | 4063 | dfs(root); |
michael@0 | 4064 | } |
michael@0 | 4065 | |
michael@0 | 4066 | },{"cp-data":5}],41:[function(require,module,exports){ |
michael@0 | 4067 | /* jshint -W079 */ |
michael@0 | 4068 | var Set = require("cp-data").Set; |
michael@0 | 4069 | /* jshint +W079 */ |
michael@0 | 4070 | |
michael@0 | 4071 | module.exports = preorder; |
michael@0 | 4072 | |
michael@0 | 4073 | // Preorder traversal of g, calling f for each visited node. Assumes the graph |
michael@0 | 4074 | // is a tree. |
michael@0 | 4075 | function preorder(g, root, f) { |
michael@0 | 4076 | var visited = new Set(); |
michael@0 | 4077 | if (g.isDirected()) { |
michael@0 | 4078 | throw new Error("This function only works for undirected graphs"); |
michael@0 | 4079 | } |
michael@0 | 4080 | function dfs(u, prev) { |
michael@0 | 4081 | if (visited.has(u)) { |
michael@0 | 4082 | throw new Error("The input graph is not a tree: " + g); |
michael@0 | 4083 | } |
michael@0 | 4084 | visited.add(u); |
michael@0 | 4085 | f(u); |
michael@0 | 4086 | g.neighbors(u).forEach(function(v) { |
michael@0 | 4087 | if (v !== prev) dfs(v, u); |
michael@0 | 4088 | }); |
michael@0 | 4089 | } |
michael@0 | 4090 | dfs(root); |
michael@0 | 4091 | } |
michael@0 | 4092 | |
michael@0 | 4093 | },{"cp-data":5}],42:[function(require,module,exports){ |
michael@0 | 4094 | var Graph = require("../Graph"), |
michael@0 | 4095 | PriorityQueue = require("cp-data").PriorityQueue; |
michael@0 | 4096 | |
michael@0 | 4097 | module.exports = prim; |
michael@0 | 4098 | |
michael@0 | 4099 | /** |
michael@0 | 4100 | * [Prim's algorithm][] takes a connected undirected graph and generates a |
michael@0 | 4101 | * [minimum spanning tree][]. This function returns the minimum spanning |
michael@0 | 4102 | * tree as an undirected graph. This algorithm is derived from the description |
michael@0 | 4103 | * in "Introduction to Algorithms", Third Edition, Cormen, et al., Pg 634. |
michael@0 | 4104 | * |
michael@0 | 4105 | * This function takes a `weightFunc(e)` which returns the weight of the edge |
michael@0 | 4106 | * `e`. It throws an Error if the graph is not connected. |
michael@0 | 4107 | * |
michael@0 | 4108 | * This function takes `O(|E| log |V|)` time. |
michael@0 | 4109 | * |
michael@0 | 4110 | * [Prim's algorithm]: https://en.wikipedia.org/wiki/Prim's_algorithm |
michael@0 | 4111 | * [minimum spanning tree]: https://en.wikipedia.org/wiki/Minimum_spanning_tree |
michael@0 | 4112 | * |
michael@0 | 4113 | * @param {Graph} g the graph used to generate the minimum spanning tree |
michael@0 | 4114 | * @param {Function} weightFunc the weight function to use |
michael@0 | 4115 | */ |
michael@0 | 4116 | function prim(g, weightFunc) { |
michael@0 | 4117 | var result = new Graph(), |
michael@0 | 4118 | parents = {}, |
michael@0 | 4119 | pq = new PriorityQueue(), |
michael@0 | 4120 | u; |
michael@0 | 4121 | |
michael@0 | 4122 | function updateNeighbors(e) { |
michael@0 | 4123 | var incidentNodes = g.incidentNodes(e), |
michael@0 | 4124 | v = incidentNodes[0] !== u ? incidentNodes[0] : incidentNodes[1], |
michael@0 | 4125 | pri = pq.priority(v); |
michael@0 | 4126 | if (pri !== undefined) { |
michael@0 | 4127 | var edgeWeight = weightFunc(e); |
michael@0 | 4128 | if (edgeWeight < pri) { |
michael@0 | 4129 | parents[v] = u; |
michael@0 | 4130 | pq.decrease(v, edgeWeight); |
michael@0 | 4131 | } |
michael@0 | 4132 | } |
michael@0 | 4133 | } |
michael@0 | 4134 | |
michael@0 | 4135 | if (g.order() === 0) { |
michael@0 | 4136 | return result; |
michael@0 | 4137 | } |
michael@0 | 4138 | |
michael@0 | 4139 | g.eachNode(function(u) { |
michael@0 | 4140 | pq.add(u, Number.POSITIVE_INFINITY); |
michael@0 | 4141 | result.addNode(u); |
michael@0 | 4142 | }); |
michael@0 | 4143 | |
michael@0 | 4144 | // Start from an arbitrary node |
michael@0 | 4145 | pq.decrease(g.nodes()[0], 0); |
michael@0 | 4146 | |
michael@0 | 4147 | var init = false; |
michael@0 | 4148 | while (pq.size() > 0) { |
michael@0 | 4149 | u = pq.removeMin(); |
michael@0 | 4150 | if (u in parents) { |
michael@0 | 4151 | result.addEdge(null, u, parents[u]); |
michael@0 | 4152 | } else if (init) { |
michael@0 | 4153 | throw new Error("Input graph is not connected: " + g); |
michael@0 | 4154 | } else { |
michael@0 | 4155 | init = true; |
michael@0 | 4156 | } |
michael@0 | 4157 | |
michael@0 | 4158 | g.incidentEdges(u).forEach(updateNeighbors); |
michael@0 | 4159 | } |
michael@0 | 4160 | |
michael@0 | 4161 | return result; |
michael@0 | 4162 | } |
michael@0 | 4163 | |
michael@0 | 4164 | },{"../Graph":33,"cp-data":5}],43:[function(require,module,exports){ |
michael@0 | 4165 | module.exports = tarjan; |
michael@0 | 4166 | |
michael@0 | 4167 | /** |
michael@0 | 4168 | * This function is an implementation of [Tarjan's algorithm][] which finds |
michael@0 | 4169 | * all [strongly connected components][] in the directed graph **g**. Each |
michael@0 | 4170 | * strongly connected component is composed of nodes that can reach all other |
michael@0 | 4171 | * nodes in the component via directed edges. A strongly connected component |
michael@0 | 4172 | * can consist of a single node if that node cannot both reach and be reached |
michael@0 | 4173 | * by any other specific node in the graph. Components of more than one node |
michael@0 | 4174 | * are guaranteed to have at least one cycle. |
michael@0 | 4175 | * |
michael@0 | 4176 | * This function returns an array of components. Each component is itself an |
michael@0 | 4177 | * array that contains the ids of all nodes in the component. |
michael@0 | 4178 | * |
michael@0 | 4179 | * [Tarjan's algorithm]: http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm |
michael@0 | 4180 | * [strongly connected components]: http://en.wikipedia.org/wiki/Strongly_connected_component |
michael@0 | 4181 | * |
michael@0 | 4182 | * @param {Digraph} g the graph to search for strongly connected components |
michael@0 | 4183 | */ |
michael@0 | 4184 | function tarjan(g) { |
michael@0 | 4185 | if (!g.isDirected()) { |
michael@0 | 4186 | throw new Error("tarjan can only be applied to a directed graph. Bad input: " + g); |
michael@0 | 4187 | } |
michael@0 | 4188 | |
michael@0 | 4189 | var index = 0, |
michael@0 | 4190 | stack = [], |
michael@0 | 4191 | visited = {}, // node id -> { onStack, lowlink, index } |
michael@0 | 4192 | results = []; |
michael@0 | 4193 | |
michael@0 | 4194 | function dfs(u) { |
michael@0 | 4195 | var entry = visited[u] = { |
michael@0 | 4196 | onStack: true, |
michael@0 | 4197 | lowlink: index, |
michael@0 | 4198 | index: index++ |
michael@0 | 4199 | }; |
michael@0 | 4200 | stack.push(u); |
michael@0 | 4201 | |
michael@0 | 4202 | g.successors(u).forEach(function(v) { |
michael@0 | 4203 | if (!(v in visited)) { |
michael@0 | 4204 | dfs(v); |
michael@0 | 4205 | entry.lowlink = Math.min(entry.lowlink, visited[v].lowlink); |
michael@0 | 4206 | } else if (visited[v].onStack) { |
michael@0 | 4207 | entry.lowlink = Math.min(entry.lowlink, visited[v].index); |
michael@0 | 4208 | } |
michael@0 | 4209 | }); |
michael@0 | 4210 | |
michael@0 | 4211 | if (entry.lowlink === entry.index) { |
michael@0 | 4212 | var cmpt = [], |
michael@0 | 4213 | v; |
michael@0 | 4214 | do { |
michael@0 | 4215 | v = stack.pop(); |
michael@0 | 4216 | visited[v].onStack = false; |
michael@0 | 4217 | cmpt.push(v); |
michael@0 | 4218 | } while (u !== v); |
michael@0 | 4219 | results.push(cmpt); |
michael@0 | 4220 | } |
michael@0 | 4221 | } |
michael@0 | 4222 | |
michael@0 | 4223 | g.nodes().forEach(function(u) { |
michael@0 | 4224 | if (!(u in visited)) { |
michael@0 | 4225 | dfs(u); |
michael@0 | 4226 | } |
michael@0 | 4227 | }); |
michael@0 | 4228 | |
michael@0 | 4229 | return results; |
michael@0 | 4230 | } |
michael@0 | 4231 | |
michael@0 | 4232 | },{}],44:[function(require,module,exports){ |
michael@0 | 4233 | module.exports = topsort; |
michael@0 | 4234 | topsort.CycleException = CycleException; |
michael@0 | 4235 | |
michael@0 | 4236 | /* |
michael@0 | 4237 | * Given a graph **g**, this function returns an ordered list of nodes such |
michael@0 | 4238 | * that for each edge `u -> v`, `u` appears before `v` in the list. If the |
michael@0 | 4239 | * graph has a cycle it is impossible to generate such a list and |
michael@0 | 4240 | * **CycleException** is thrown. |
michael@0 | 4241 | * |
michael@0 | 4242 | * See [topological sorting](https://en.wikipedia.org/wiki/Topological_sorting) |
michael@0 | 4243 | * for more details about how this algorithm works. |
michael@0 | 4244 | * |
michael@0 | 4245 | * @param {Digraph} g the graph to sort |
michael@0 | 4246 | */ |
michael@0 | 4247 | function topsort(g) { |
michael@0 | 4248 | if (!g.isDirected()) { |
michael@0 | 4249 | throw new Error("topsort can only be applied to a directed graph. Bad input: " + g); |
michael@0 | 4250 | } |
michael@0 | 4251 | |
michael@0 | 4252 | var visited = {}; |
michael@0 | 4253 | var stack = {}; |
michael@0 | 4254 | var results = []; |
michael@0 | 4255 | |
michael@0 | 4256 | function visit(node) { |
michael@0 | 4257 | if (node in stack) { |
michael@0 | 4258 | throw new CycleException(); |
michael@0 | 4259 | } |
michael@0 | 4260 | |
michael@0 | 4261 | if (!(node in visited)) { |
michael@0 | 4262 | stack[node] = true; |
michael@0 | 4263 | visited[node] = true; |
michael@0 | 4264 | g.predecessors(node).forEach(function(pred) { |
michael@0 | 4265 | visit(pred); |
michael@0 | 4266 | }); |
michael@0 | 4267 | delete stack[node]; |
michael@0 | 4268 | results.push(node); |
michael@0 | 4269 | } |
michael@0 | 4270 | } |
michael@0 | 4271 | |
michael@0 | 4272 | var sinks = g.sinks(); |
michael@0 | 4273 | if (g.order() !== 0 && sinks.length === 0) { |
michael@0 | 4274 | throw new CycleException(); |
michael@0 | 4275 | } |
michael@0 | 4276 | |
michael@0 | 4277 | g.sinks().forEach(function(sink) { |
michael@0 | 4278 | visit(sink); |
michael@0 | 4279 | }); |
michael@0 | 4280 | |
michael@0 | 4281 | return results; |
michael@0 | 4282 | } |
michael@0 | 4283 | |
michael@0 | 4284 | function CycleException() {} |
michael@0 | 4285 | |
michael@0 | 4286 | CycleException.prototype.toString = function() { |
michael@0 | 4287 | return "Graph has at least one cycle"; |
michael@0 | 4288 | }; |
michael@0 | 4289 | |
michael@0 | 4290 | },{}],45:[function(require,module,exports){ |
michael@0 | 4291 | // This file provides a helper function that mixes-in Dot behavior to an |
michael@0 | 4292 | // existing graph prototype. |
michael@0 | 4293 | |
michael@0 | 4294 | /* jshint -W079 */ |
michael@0 | 4295 | var Set = require("cp-data").Set; |
michael@0 | 4296 | /* jshint +W079 */ |
michael@0 | 4297 | |
michael@0 | 4298 | module.exports = compoundify; |
michael@0 | 4299 | |
michael@0 | 4300 | // Extends the given SuperConstructor with the ability for nodes to contain |
michael@0 | 4301 | // other nodes. A special node id `null` is used to indicate the root graph. |
michael@0 | 4302 | function compoundify(SuperConstructor) { |
michael@0 | 4303 | function Constructor() { |
michael@0 | 4304 | SuperConstructor.call(this); |
michael@0 | 4305 | |
michael@0 | 4306 | // Map of object id -> parent id (or null for root graph) |
michael@0 | 4307 | this._parents = {}; |
michael@0 | 4308 | |
michael@0 | 4309 | // Map of id (or null) -> children set |
michael@0 | 4310 | this._children = {}; |
michael@0 | 4311 | this._children[null] = new Set(); |
michael@0 | 4312 | } |
michael@0 | 4313 | |
michael@0 | 4314 | Constructor.prototype = new SuperConstructor(); |
michael@0 | 4315 | Constructor.prototype.constructor = Constructor; |
michael@0 | 4316 | |
michael@0 | 4317 | Constructor.prototype.parent = function(u, parent) { |
michael@0 | 4318 | this._strictGetNode(u); |
michael@0 | 4319 | |
michael@0 | 4320 | if (arguments.length < 2) { |
michael@0 | 4321 | return this._parents[u]; |
michael@0 | 4322 | } |
michael@0 | 4323 | |
michael@0 | 4324 | if (u === parent) { |
michael@0 | 4325 | throw new Error("Cannot make " + u + " a parent of itself"); |
michael@0 | 4326 | } |
michael@0 | 4327 | if (parent !== null) { |
michael@0 | 4328 | this._strictGetNode(parent); |
michael@0 | 4329 | } |
michael@0 | 4330 | |
michael@0 | 4331 | this._children[this._parents[u]].remove(u); |
michael@0 | 4332 | this._parents[u] = parent; |
michael@0 | 4333 | this._children[parent].add(u); |
michael@0 | 4334 | }; |
michael@0 | 4335 | |
michael@0 | 4336 | Constructor.prototype.children = function(u) { |
michael@0 | 4337 | if (u !== null) { |
michael@0 | 4338 | this._strictGetNode(u); |
michael@0 | 4339 | } |
michael@0 | 4340 | return this._children[u].keys(); |
michael@0 | 4341 | }; |
michael@0 | 4342 | |
michael@0 | 4343 | Constructor.prototype.addNode = function(u, value) { |
michael@0 | 4344 | u = SuperConstructor.prototype.addNode.call(this, u, value); |
michael@0 | 4345 | this._parents[u] = null; |
michael@0 | 4346 | this._children[u] = new Set(); |
michael@0 | 4347 | this._children[null].add(u); |
michael@0 | 4348 | return u; |
michael@0 | 4349 | }; |
michael@0 | 4350 | |
michael@0 | 4351 | Constructor.prototype.delNode = function(u) { |
michael@0 | 4352 | // Promote all children to the parent of the subgraph |
michael@0 | 4353 | var parent = this.parent(u); |
michael@0 | 4354 | this._children[u].keys().forEach(function(child) { |
michael@0 | 4355 | this.parent(child, parent); |
michael@0 | 4356 | }, this); |
michael@0 | 4357 | |
michael@0 | 4358 | this._children[parent].remove(u); |
michael@0 | 4359 | delete this._parents[u]; |
michael@0 | 4360 | delete this._children[u]; |
michael@0 | 4361 | |
michael@0 | 4362 | return SuperConstructor.prototype.delNode.call(this, u); |
michael@0 | 4363 | }; |
michael@0 | 4364 | |
michael@0 | 4365 | Constructor.prototype.copy = function() { |
michael@0 | 4366 | var copy = SuperConstructor.prototype.copy.call(this); |
michael@0 | 4367 | this.nodes().forEach(function(u) { |
michael@0 | 4368 | copy.parent(u, this.parent(u)); |
michael@0 | 4369 | }, this); |
michael@0 | 4370 | return copy; |
michael@0 | 4371 | }; |
michael@0 | 4372 | |
michael@0 | 4373 | Constructor.prototype.filterNodes = function(filter) { |
michael@0 | 4374 | var self = this, |
michael@0 | 4375 | copy = SuperConstructor.prototype.filterNodes.call(this, filter); |
michael@0 | 4376 | |
michael@0 | 4377 | var parents = {}; |
michael@0 | 4378 | function findParent(u) { |
michael@0 | 4379 | var parent = self.parent(u); |
michael@0 | 4380 | if (parent === null || copy.hasNode(parent)) { |
michael@0 | 4381 | parents[u] = parent; |
michael@0 | 4382 | return parent; |
michael@0 | 4383 | } else if (parent in parents) { |
michael@0 | 4384 | return parents[parent]; |
michael@0 | 4385 | } else { |
michael@0 | 4386 | return findParent(parent); |
michael@0 | 4387 | } |
michael@0 | 4388 | } |
michael@0 | 4389 | |
michael@0 | 4390 | copy.eachNode(function(u) { copy.parent(u, findParent(u)); }); |
michael@0 | 4391 | |
michael@0 | 4392 | return copy; |
michael@0 | 4393 | }; |
michael@0 | 4394 | |
michael@0 | 4395 | return Constructor; |
michael@0 | 4396 | } |
michael@0 | 4397 | |
michael@0 | 4398 | },{"cp-data":5}],46:[function(require,module,exports){ |
michael@0 | 4399 | var Graph = require("../Graph"), |
michael@0 | 4400 | Digraph = require("../Digraph"), |
michael@0 | 4401 | CGraph = require("../CGraph"), |
michael@0 | 4402 | CDigraph = require("../CDigraph"); |
michael@0 | 4403 | |
michael@0 | 4404 | exports.decode = function(nodes, edges, Ctor) { |
michael@0 | 4405 | Ctor = Ctor || Digraph; |
michael@0 | 4406 | |
michael@0 | 4407 | if (typeOf(nodes) !== "Array") { |
michael@0 | 4408 | throw new Error("nodes is not an Array"); |
michael@0 | 4409 | } |
michael@0 | 4410 | |
michael@0 | 4411 | if (typeOf(edges) !== "Array") { |
michael@0 | 4412 | throw new Error("edges is not an Array"); |
michael@0 | 4413 | } |
michael@0 | 4414 | |
michael@0 | 4415 | if (typeof Ctor === "string") { |
michael@0 | 4416 | switch(Ctor) { |
michael@0 | 4417 | case "graph": Ctor = Graph; break; |
michael@0 | 4418 | case "digraph": Ctor = Digraph; break; |
michael@0 | 4419 | case "cgraph": Ctor = CGraph; break; |
michael@0 | 4420 | case "cdigraph": Ctor = CDigraph; break; |
michael@0 | 4421 | default: throw new Error("Unrecognized graph type: " + Ctor); |
michael@0 | 4422 | } |
michael@0 | 4423 | } |
michael@0 | 4424 | |
michael@0 | 4425 | var graph = new Ctor(); |
michael@0 | 4426 | |
michael@0 | 4427 | nodes.forEach(function(u) { |
michael@0 | 4428 | graph.addNode(u.id, u.value); |
michael@0 | 4429 | }); |
michael@0 | 4430 | |
michael@0 | 4431 | // If the graph is compound, set up children... |
michael@0 | 4432 | if (graph.parent) { |
michael@0 | 4433 | nodes.forEach(function(u) { |
michael@0 | 4434 | if (u.children) { |
michael@0 | 4435 | u.children.forEach(function(v) { |
michael@0 | 4436 | graph.parent(v, u.id); |
michael@0 | 4437 | }); |
michael@0 | 4438 | } |
michael@0 | 4439 | }); |
michael@0 | 4440 | } |
michael@0 | 4441 | |
michael@0 | 4442 | edges.forEach(function(e) { |
michael@0 | 4443 | graph.addEdge(e.id, e.u, e.v, e.value); |
michael@0 | 4444 | }); |
michael@0 | 4445 | |
michael@0 | 4446 | return graph; |
michael@0 | 4447 | }; |
michael@0 | 4448 | |
michael@0 | 4449 | exports.encode = function(graph) { |
michael@0 | 4450 | var nodes = []; |
michael@0 | 4451 | var edges = []; |
michael@0 | 4452 | |
michael@0 | 4453 | graph.eachNode(function(u, value) { |
michael@0 | 4454 | var node = {id: u, value: value}; |
michael@0 | 4455 | if (graph.children) { |
michael@0 | 4456 | var children = graph.children(u); |
michael@0 | 4457 | if (children.length) { |
michael@0 | 4458 | node.children = children; |
michael@0 | 4459 | } |
michael@0 | 4460 | } |
michael@0 | 4461 | nodes.push(node); |
michael@0 | 4462 | }); |
michael@0 | 4463 | |
michael@0 | 4464 | graph.eachEdge(function(e, u, v, value) { |
michael@0 | 4465 | edges.push({id: e, u: u, v: v, value: value}); |
michael@0 | 4466 | }); |
michael@0 | 4467 | |
michael@0 | 4468 | var type; |
michael@0 | 4469 | if (graph instanceof CDigraph) { |
michael@0 | 4470 | type = "cdigraph"; |
michael@0 | 4471 | } else if (graph instanceof CGraph) { |
michael@0 | 4472 | type = "cgraph"; |
michael@0 | 4473 | } else if (graph instanceof Digraph) { |
michael@0 | 4474 | type = "digraph"; |
michael@0 | 4475 | } else if (graph instanceof Graph) { |
michael@0 | 4476 | type = "graph"; |
michael@0 | 4477 | } else { |
michael@0 | 4478 | throw new Error("Couldn't determine type of graph: " + graph); |
michael@0 | 4479 | } |
michael@0 | 4480 | |
michael@0 | 4481 | return { nodes: nodes, edges: edges, type: type }; |
michael@0 | 4482 | }; |
michael@0 | 4483 | |
michael@0 | 4484 | function typeOf(obj) { |
michael@0 | 4485 | return Object.prototype.toString.call(obj).slice(8, -1); |
michael@0 | 4486 | } |
michael@0 | 4487 | |
michael@0 | 4488 | },{"../CDigraph":30,"../CGraph":31,"../Digraph":32,"../Graph":33}],47:[function(require,module,exports){ |
michael@0 | 4489 | /* jshint -W079 */ |
michael@0 | 4490 | var Set = require("cp-data").Set; |
michael@0 | 4491 | /* jshint +W079 */ |
michael@0 | 4492 | |
michael@0 | 4493 | exports.all = function() { |
michael@0 | 4494 | return function() { return true; }; |
michael@0 | 4495 | }; |
michael@0 | 4496 | |
michael@0 | 4497 | exports.nodesFromList = function(nodes) { |
michael@0 | 4498 | var set = new Set(nodes); |
michael@0 | 4499 | return function(u) { |
michael@0 | 4500 | return set.has(u); |
michael@0 | 4501 | }; |
michael@0 | 4502 | }; |
michael@0 | 4503 | |
michael@0 | 4504 | },{"cp-data":5}],48:[function(require,module,exports){ |
michael@0 | 4505 | var Graph = require("./Graph"), |
michael@0 | 4506 | Digraph = require("./Digraph"); |
michael@0 | 4507 | |
michael@0 | 4508 | // Side-effect based changes are lousy, but node doesn't seem to resolve the |
michael@0 | 4509 | // requires cycle. |
michael@0 | 4510 | |
michael@0 | 4511 | /** |
michael@0 | 4512 | * Returns a new directed graph using the nodes and edges from this graph. The |
michael@0 | 4513 | * new graph will have the same nodes, but will have twice the number of edges: |
michael@0 | 4514 | * each edge is split into two edges with opposite directions. Edge ids, |
michael@0 | 4515 | * consequently, are not preserved by this transformation. |
michael@0 | 4516 | */ |
michael@0 | 4517 | Graph.prototype.toDigraph = |
michael@0 | 4518 | Graph.prototype.asDirected = function() { |
michael@0 | 4519 | var g = new Digraph(); |
michael@0 | 4520 | this.eachNode(function(u, value) { g.addNode(u, value); }); |
michael@0 | 4521 | this.eachEdge(function(e, u, v, value) { |
michael@0 | 4522 | g.addEdge(null, u, v, value); |
michael@0 | 4523 | g.addEdge(null, v, u, value); |
michael@0 | 4524 | }); |
michael@0 | 4525 | return g; |
michael@0 | 4526 | }; |
michael@0 | 4527 | |
michael@0 | 4528 | /** |
michael@0 | 4529 | * Returns a new undirected graph using the nodes and edges from this graph. |
michael@0 | 4530 | * The new graph will have the same nodes, but the edges will be made |
michael@0 | 4531 | * undirected. Edge ids are preserved in this transformation. |
michael@0 | 4532 | */ |
michael@0 | 4533 | Digraph.prototype.toGraph = |
michael@0 | 4534 | Digraph.prototype.asUndirected = function() { |
michael@0 | 4535 | var g = new Graph(); |
michael@0 | 4536 | this.eachNode(function(u, value) { g.addNode(u, value); }); |
michael@0 | 4537 | this.eachEdge(function(e, u, v, value) { |
michael@0 | 4538 | g.addEdge(e, u, v, value); |
michael@0 | 4539 | }); |
michael@0 | 4540 | return g; |
michael@0 | 4541 | }; |
michael@0 | 4542 | |
michael@0 | 4543 | },{"./Digraph":32,"./Graph":33}],49:[function(require,module,exports){ |
michael@0 | 4544 | // Returns an array of all values for properties of **o**. |
michael@0 | 4545 | exports.values = function(o) { |
michael@0 | 4546 | var ks = Object.keys(o), |
michael@0 | 4547 | len = ks.length, |
michael@0 | 4548 | result = new Array(len), |
michael@0 | 4549 | i; |
michael@0 | 4550 | for (i = 0; i < len; ++i) { |
michael@0 | 4551 | result[i] = o[ks[i]]; |
michael@0 | 4552 | } |
michael@0 | 4553 | return result; |
michael@0 | 4554 | }; |
michael@0 | 4555 | |
michael@0 | 4556 | },{}],50:[function(require,module,exports){ |
michael@0 | 4557 | module.exports = '0.7.4'; |
michael@0 | 4558 | |
michael@0 | 4559 | },{}]},{},[1]) |
michael@0 | 4560 | ; |