browser/devtools/webaudioeditor/lib/dagre-d3.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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 ;

mercurial