mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 12:37:37 +00:00
4560 lines
128 KiB
JavaScript
4560 lines
128 KiB
JavaScript
;(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){
|
||
var global=self;/**
|
||
* @license
|
||
* Copyright (c) 2012-2013 Chris Pettitt
|
||
*
|
||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
* of this software and associated documentation files (the "Software"), to deal
|
||
* in the Software without restriction, including without limitation the rights
|
||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
* copies of the Software, and to permit persons to whom the Software is
|
||
* furnished to do so, subject to the following conditions:
|
||
*
|
||
* The above copyright notice and this permission notice shall be included in
|
||
* all copies or substantial portions of the Software.
|
||
*
|
||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
* THE SOFTWARE.
|
||
*/
|
||
global.dagreD3 = require('./index');
|
||
|
||
},{"./index":2}],2:[function(require,module,exports){
|
||
/**
|
||
* @license
|
||
* Copyright (c) 2012-2013 Chris Pettitt
|
||
*
|
||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
* of this software and associated documentation files (the "Software"), to deal
|
||
* in the Software without restriction, including without limitation the rights
|
||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
* copies of the Software, and to permit persons to whom the Software is
|
||
* furnished to do so, subject to the following conditions:
|
||
*
|
||
* The above copyright notice and this permission notice shall be included in
|
||
* all copies or substantial portions of the Software.
|
||
*
|
||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
* THE SOFTWARE.
|
||
*/
|
||
module.exports = {
|
||
Digraph: require('graphlib').Digraph,
|
||
Renderer: require('./lib/Renderer'),
|
||
json: require('graphlib').converter.json,
|
||
layout: require('dagre').layout,
|
||
version: require('./lib/version')
|
||
};
|
||
|
||
},{"./lib/Renderer":3,"./lib/version":4,"dagre":11,"graphlib":28}],3:[function(require,module,exports){
|
||
var layout = require('dagre').layout;
|
||
|
||
var d3;
|
||
try { d3 = require('d3'); } catch (_) { d3 = window.d3; }
|
||
|
||
module.exports = Renderer;
|
||
|
||
function Renderer() {
|
||
// Set up defaults...
|
||
this._layout = layout();
|
||
|
||
this.drawNodes(defaultDrawNodes);
|
||
this.drawEdgeLabels(defaultDrawEdgeLabels);
|
||
this.drawEdgePaths(defaultDrawEdgePaths);
|
||
this.positionNodes(defaultPositionNodes);
|
||
this.positionEdgeLabels(defaultPositionEdgeLabels);
|
||
this.positionEdgePaths(defaultPositionEdgePaths);
|
||
this.transition(defaultTransition);
|
||
this.postLayout(defaultPostLayout);
|
||
this.postRender(defaultPostRender);
|
||
|
||
this.edgeInterpolate('bundle');
|
||
this.edgeTension(0.95);
|
||
}
|
||
|
||
Renderer.prototype.layout = function(layout) {
|
||
if (!arguments.length) { return this._layout; }
|
||
this._layout = layout;
|
||
return this;
|
||
};
|
||
|
||
Renderer.prototype.drawNodes = function(drawNodes) {
|
||
if (!arguments.length) { return this._drawNodes; }
|
||
this._drawNodes = bind(drawNodes, this);
|
||
return this;
|
||
};
|
||
|
||
Renderer.prototype.drawEdgeLabels = function(drawEdgeLabels) {
|
||
if (!arguments.length) { return this._drawEdgeLabels; }
|
||
this._drawEdgeLabels = bind(drawEdgeLabels, this);
|
||
return this;
|
||
};
|
||
|
||
Renderer.prototype.drawEdgePaths = function(drawEdgePaths) {
|
||
if (!arguments.length) { return this._drawEdgePaths; }
|
||
this._drawEdgePaths = bind(drawEdgePaths, this);
|
||
return this;
|
||
};
|
||
|
||
Renderer.prototype.positionNodes = function(positionNodes) {
|
||
if (!arguments.length) { return this._positionNodes; }
|
||
this._positionNodes = bind(positionNodes, this);
|
||
return this;
|
||
};
|
||
|
||
Renderer.prototype.positionEdgeLabels = function(positionEdgeLabels) {
|
||
if (!arguments.length) { return this._positionEdgeLabels; }
|
||
this._positionEdgeLabels = bind(positionEdgeLabels, this);
|
||
return this;
|
||
};
|
||
|
||
Renderer.prototype.positionEdgePaths = function(positionEdgePaths) {
|
||
if (!arguments.length) { return this._positionEdgePaths; }
|
||
this._positionEdgePaths = bind(positionEdgePaths, this);
|
||
return this;
|
||
};
|
||
|
||
Renderer.prototype.transition = function(transition) {
|
||
if (!arguments.length) { return this._transition; }
|
||
this._transition = bind(transition, this);
|
||
return this;
|
||
};
|
||
|
||
Renderer.prototype.postLayout = function(postLayout) {
|
||
if (!arguments.length) { return this._postLayout; }
|
||
this._postLayout = bind(postLayout, this);
|
||
return this;
|
||
};
|
||
|
||
Renderer.prototype.postRender = function(postRender) {
|
||
if (!arguments.length) { return this._postRender; }
|
||
this._postRender = bind(postRender, this);
|
||
return this;
|
||
};
|
||
|
||
Renderer.prototype.edgeInterpolate = function(edgeInterpolate) {
|
||
if (!arguments.length) { return this._edgeInterpolate; }
|
||
this._edgeInterpolate = edgeInterpolate;
|
||
return this;
|
||
};
|
||
|
||
Renderer.prototype.edgeTension = function(edgeTension) {
|
||
if (!arguments.length) { return this._edgeTension; }
|
||
this._edgeTension = edgeTension;
|
||
return this;
|
||
};
|
||
|
||
Renderer.prototype.run = function(graph, svg) {
|
||
// First copy the input graph so that it is not changed by the rendering
|
||
// process.
|
||
graph = copyAndInitGraph(graph);
|
||
|
||
// Create layers
|
||
svg
|
||
.selectAll('g.edgePaths, g.edgeLabels, g.nodes')
|
||
.data(['edgePaths', 'edgeLabels', 'nodes'])
|
||
.enter()
|
||
.append('g')
|
||
.attr('class', function(d) { return d; });
|
||
|
||
|
||
// Create node and edge roots, attach labels, and capture dimension
|
||
// information for use with layout.
|
||
var svgNodes = this._drawNodes(graph, svg.select('g.nodes'));
|
||
var svgEdgeLabels = this._drawEdgeLabels(graph, svg.select('g.edgeLabels'));
|
||
|
||
svgNodes.each(function(u) { calculateDimensions(this, graph.node(u)); });
|
||
svgEdgeLabels.each(function(e) { calculateDimensions(this, graph.edge(e)); });
|
||
|
||
// Now apply the layout function
|
||
var result = runLayout(graph, this._layout);
|
||
|
||
// Run any user-specified post layout processing
|
||
this._postLayout(result, svg);
|
||
|
||
var svgEdgePaths = this._drawEdgePaths(graph, svg.select('g.edgePaths'));
|
||
|
||
// Apply the layout information to the graph
|
||
this._positionNodes(result, svgNodes);
|
||
this._positionEdgeLabels(result, svgEdgeLabels);
|
||
this._positionEdgePaths(result, svgEdgePaths);
|
||
|
||
this._postRender(result, svg);
|
||
|
||
return result;
|
||
};
|
||
|
||
function copyAndInitGraph(graph) {
|
||
var copy = graph.copy();
|
||
|
||
// Init labels if they were not present in the source graph
|
||
copy.nodes().forEach(function(u) {
|
||
var value = copy.node(u);
|
||
if (value === undefined) {
|
||
value = {};
|
||
copy.node(u, value);
|
||
}
|
||
if (!('label' in value)) { value.label = ''; }
|
||
});
|
||
|
||
copy.edges().forEach(function(e) {
|
||
var value = copy.edge(e);
|
||
if (value === undefined) {
|
||
value = {};
|
||
copy.edge(e, value);
|
||
}
|
||
if (!('label' in value)) { value.label = ''; }
|
||
});
|
||
|
||
return copy;
|
||
}
|
||
|
||
function calculateDimensions(group, value) {
|
||
var bbox = group.getBBox();
|
||
value.width = bbox.width;
|
||
value.height = bbox.height;
|
||
}
|
||
|
||
function runLayout(graph, layout) {
|
||
var result = layout.run(graph);
|
||
|
||
// Copy labels to the result graph
|
||
graph.eachNode(function(u, value) { result.node(u).label = value.label; });
|
||
graph.eachEdge(function(e, u, v, value) { result.edge(e).label = value.label; });
|
||
|
||
return result;
|
||
}
|
||
|
||
function defaultDrawNodes(g, root) {
|
||
var nodes = g.nodes().filter(function(u) { return !isComposite(g, u); });
|
||
|
||
var svgNodes = root
|
||
.selectAll('g.node')
|
||
.classed('enter', false)
|
||
.data(nodes, function(u) { return u; });
|
||
|
||
svgNodes.selectAll('*').remove();
|
||
|
||
svgNodes
|
||
.enter()
|
||
.append('g')
|
||
.style('opacity', 0)
|
||
.attr('class', 'node enter');
|
||
|
||
svgNodes.each(function(u) { addLabel(g.node(u), d3.select(this), 10, 10); });
|
||
|
||
this._transition(svgNodes.exit())
|
||
.style('opacity', 0)
|
||
.remove();
|
||
|
||
return svgNodes;
|
||
}
|
||
|
||
function defaultDrawEdgeLabels(g, root) {
|
||
var svgEdgeLabels = root
|
||
.selectAll('g.edgeLabel')
|
||
.classed('enter', false)
|
||
.data(g.edges(), function (e) { return e; });
|
||
|
||
svgEdgeLabels.selectAll('*').remove();
|
||
|
||
svgEdgeLabels
|
||
.enter()
|
||
.append('g')
|
||
.style('opacity', 0)
|
||
.attr('class', 'edgeLabel enter');
|
||
|
||
svgEdgeLabels.each(function(e) { addLabel(g.edge(e), d3.select(this), 0, 0); });
|
||
|
||
this._transition(svgEdgeLabels.exit())
|
||
.style('opacity', 0)
|
||
.remove();
|
||
|
||
return svgEdgeLabels;
|
||
}
|
||
|
||
var defaultDrawEdgePaths = function(g, root) {
|
||
var svgEdgePaths = root
|
||
.selectAll('g.edgePath')
|
||
.classed('enter', false)
|
||
.data(g.edges(), function(e) { return e; });
|
||
|
||
svgEdgePaths
|
||
.enter()
|
||
.append('g')
|
||
.attr('class', 'edgePath enter')
|
||
.append('path')
|
||
.style('opacity', 0)
|
||
.attr('marker-end', 'url(#arrowhead)');
|
||
|
||
this._transition(svgEdgePaths.exit())
|
||
.style('opacity', 0)
|
||
.remove();
|
||
|
||
return svgEdgePaths;
|
||
};
|
||
|
||
function defaultPositionNodes(g, svgNodes, svgNodesEnter) {
|
||
function transform(u) {
|
||
var value = g.node(u);
|
||
return 'translate(' + value.x + ',' + value.y + ')';
|
||
}
|
||
|
||
// For entering nodes, position immediately without transition
|
||
svgNodes.filter('.enter').attr('transform', transform);
|
||
|
||
this._transition(svgNodes)
|
||
.style('opacity', 1)
|
||
.attr('transform', transform);
|
||
}
|
||
|
||
function defaultPositionEdgeLabels(g, svgEdgeLabels) {
|
||
function transform(e) {
|
||
var value = g.edge(e);
|
||
var point = findMidPoint(value.points);
|
||
return 'translate(' + point.x + ',' + point.y + ')';
|
||
}
|
||
|
||
// For entering edge labels, position immediately without transition
|
||
svgEdgeLabels.filter('.enter').attr('transform', transform);
|
||
|
||
this._transition(svgEdgeLabels)
|
||
.style('opacity', 1)
|
||
.attr('transform', transform);
|
||
}
|
||
|
||
function defaultPositionEdgePaths(g, svgEdgePaths) {
|
||
var interpolate = this._edgeInterpolate,
|
||
tension = this._edgeTension;
|
||
|
||
function calcPoints(e) {
|
||
var value = g.edge(e);
|
||
var source = g.node(g.incidentNodes(e)[0]);
|
||
var target = g.node(g.incidentNodes(e)[1]);
|
||
var points = value.points.slice();
|
||
|
||
var p0 = points.length === 0 ? target : points[0];
|
||
var p1 = points.length === 0 ? source : points[points.length - 1];
|
||
|
||
points.unshift(intersectRect(source, p0));
|
||
// TODO: use bpodgursky's shortening algorithm here
|
||
points.push(intersectRect(target, p1));
|
||
|
||
return d3.svg.line()
|
||
.x(function(d) { return d.x; })
|
||
.y(function(d) { return d.y; })
|
||
.interpolate(interpolate)
|
||
.tension(tension)
|
||
(points);
|
||
}
|
||
|
||
svgEdgePaths.filter('.enter').selectAll('path')
|
||
.attr('d', calcPoints);
|
||
|
||
this._transition(svgEdgePaths.selectAll('path'))
|
||
.attr('d', calcPoints)
|
||
.style('opacity', 1);
|
||
}
|
||
|
||
// By default we do not use transitions
|
||
function defaultTransition(selection) {
|
||
return selection;
|
||
}
|
||
|
||
function defaultPostLayout() {
|
||
// Do nothing
|
||
}
|
||
|
||
function defaultPostRender(graph, root) {
|
||
if (graph.isDirected() && root.select('#arrowhead').empty()) {
|
||
root
|
||
.append('svg:defs')
|
||
.append('svg:marker')
|
||
.attr('id', 'arrowhead')
|
||
.attr('viewBox', '0 0 10 10')
|
||
.attr('refX', 8)
|
||
.attr('refY', 5)
|
||
.attr('markerUnits', 'strokewidth')
|
||
.attr('markerWidth', 8)
|
||
.attr('markerHeight', 5)
|
||
.attr('orient', 'auto')
|
||
.attr('style', 'fill: #333')
|
||
.append('svg:path')
|
||
.attr('d', 'M 0 0 L 10 5 L 0 10 z');
|
||
}
|
||
}
|
||
|
||
function addLabel(node, root, marginX, marginY) {
|
||
// Add the rect first so that it appears behind the label
|
||
var label = node.label;
|
||
var rect = root.append('rect');
|
||
var labelSvg = root.append('g');
|
||
|
||
if (label[0] === '<') {
|
||
addForeignObjectLabel(label, labelSvg);
|
||
// No margin for HTML elements
|
||
marginX = marginY = 0;
|
||
} else {
|
||
addTextLabel(label,
|
||
labelSvg,
|
||
Math.floor(node.labelCols),
|
||
node.labelCut);
|
||
}
|
||
|
||
var bbox = root.node().getBBox();
|
||
|
||
labelSvg.attr('transform',
|
||
'translate(' + (-bbox.width / 2) + ',' + (-bbox.height / 2) + ')');
|
||
|
||
rect
|
||
.attr('rx', 5)
|
||
.attr('ry', 5)
|
||
.attr('x', -(bbox.width / 2 + marginX))
|
||
.attr('y', -(bbox.height / 2 + marginY))
|
||
.attr('width', bbox.width + 2 * marginX)
|
||
.attr('height', bbox.height + 2 * marginY);
|
||
}
|
||
|
||
function addForeignObjectLabel(label, root) {
|
||
var fo = root
|
||
.append('foreignObject')
|
||
.attr('width', '100000');
|
||
|
||
var w, h;
|
||
fo
|
||
.append('xhtml:div')
|
||
.style('float', 'left')
|
||
// TODO find a better way to get dimensions for foreignObjects...
|
||
.html(function() { return label; })
|
||
.each(function() {
|
||
w = this.clientWidth;
|
||
h = this.clientHeight;
|
||
});
|
||
|
||
fo
|
||
.attr('width', w)
|
||
.attr('height', h);
|
||
}
|
||
|
||
function addTextLabel(label, root, labelCols, labelCut) {
|
||
if (labelCut === undefined) labelCut = "false";
|
||
labelCut = (labelCut.toString().toLowerCase() === "true");
|
||
|
||
var node = root
|
||
.append('text')
|
||
.attr('text-anchor', 'left');
|
||
|
||
label = label.replace(/\\n/g, "\n");
|
||
|
||
var arr = labelCols ? wordwrap(label, labelCols, labelCut) : label;
|
||
arr = arr.split("\n");
|
||
for (var i = 0; i < arr.length; i++) {
|
||
node
|
||
.append('tspan')
|
||
.attr('dy', '1em')
|
||
.attr('x', '1')
|
||
.text(arr[i]);
|
||
}
|
||
}
|
||
|
||
// Thanks to
|
||
// http://james.padolsey.com/javascript/wordwrap-for-javascript/
|
||
function wordwrap (str, width, cut, brk) {
|
||
brk = brk || '\n';
|
||
width = width || 75;
|
||
cut = cut || false;
|
||
|
||
if (!str) { return str; }
|
||
|
||
var regex = '.{1,' +width+ '}(\\s|$)' + (cut ? '|.{' +width+ '}|.+$' : '|\\S+?(\\s|$)');
|
||
|
||
return str.match( RegExp(regex, 'g') ).join( brk );
|
||
}
|
||
|
||
function findMidPoint(points) {
|
||
var midIdx = points.length / 2;
|
||
if (points.length % 2) {
|
||
return points[Math.floor(midIdx)];
|
||
} else {
|
||
var p0 = points[midIdx - 1];
|
||
var p1 = points[midIdx];
|
||
return {x: (p0.x + p1.x) / 2, y: (p0.y + p1.y) / 2};
|
||
}
|
||
}
|
||
|
||
function intersectRect(rect, point) {
|
||
var x = rect.x;
|
||
var y = rect.y;
|
||
|
||
// For now we only support rectangles
|
||
|
||
// Rectangle intersection algorithm from:
|
||
// http://math.stackexchange.com/questions/108113/find-edge-between-two-boxes
|
||
var dx = point.x - x;
|
||
var dy = point.y - y;
|
||
var w = rect.width / 2;
|
||
var h = rect.height / 2;
|
||
|
||
var sx, sy;
|
||
if (Math.abs(dy) * w > Math.abs(dx) * h) {
|
||
// Intersection is top or bottom of rect.
|
||
if (dy < 0) {
|
||
h = -h;
|
||
}
|
||
sx = dy === 0 ? 0 : h * dx / dy;
|
||
sy = h;
|
||
} else {
|
||
// Intersection is left or right of rect.
|
||
if (dx < 0) {
|
||
w = -w;
|
||
}
|
||
sx = w;
|
||
sy = dx === 0 ? 0 : w * dy / dx;
|
||
}
|
||
|
||
return {x: x + sx, y: y + sy};
|
||
}
|
||
|
||
function isComposite(g, u) {
|
||
return 'children' in g && g.children(u).length;
|
||
}
|
||
|
||
function bind(func, thisArg) {
|
||
// For some reason PhantomJS occassionally fails when using the builtin bind,
|
||
// so we check if it is available and if not, use a degenerate polyfill.
|
||
if (func.bind) {
|
||
return func.bind(thisArg);
|
||
}
|
||
|
||
return function() {
|
||
return func.apply(thisArg, arguments);
|
||
};
|
||
}
|
||
|
||
},{"d3":10,"dagre":11}],4:[function(require,module,exports){
|
||
module.exports = '0.1.5';
|
||
|
||
},{}],5:[function(require,module,exports){
|
||
exports.Set = require('./lib/Set');
|
||
exports.PriorityQueue = require('./lib/PriorityQueue');
|
||
exports.version = require('./lib/version');
|
||
|
||
},{"./lib/PriorityQueue":6,"./lib/Set":7,"./lib/version":9}],6:[function(require,module,exports){
|
||
module.exports = PriorityQueue;
|
||
|
||
/**
|
||
* A min-priority queue data structure. This algorithm is derived from Cormen,
|
||
* et al., "Introduction to Algorithms". The basic idea of a min-priority
|
||
* queue is that you can efficiently (in O(1) time) get the smallest key in
|
||
* the queue. Adding and removing elements takes O(log n) time. A key can
|
||
* have its priority decreased in O(log n) time.
|
||
*/
|
||
function PriorityQueue() {
|
||
this._arr = [];
|
||
this._keyIndices = {};
|
||
}
|
||
|
||
/**
|
||
* Returns the number of elements in the queue. Takes `O(1)` time.
|
||
*/
|
||
PriorityQueue.prototype.size = function() {
|
||
return this._arr.length;
|
||
};
|
||
|
||
/**
|
||
* Returns the keys that are in the queue. Takes `O(n)` time.
|
||
*/
|
||
PriorityQueue.prototype.keys = function() {
|
||
return this._arr.map(function(x) { return x.key; });
|
||
};
|
||
|
||
/**
|
||
* Returns `true` if **key** is in the queue and `false` if not.
|
||
*/
|
||
PriorityQueue.prototype.has = function(key) {
|
||
return key in this._keyIndices;
|
||
};
|
||
|
||
/**
|
||
* Returns the priority for **key**. If **key** is not present in the queue
|
||
* then this function returns `undefined`. Takes `O(1)` time.
|
||
*
|
||
* @param {Object} key
|
||
*/
|
||
PriorityQueue.prototype.priority = function(key) {
|
||
var index = this._keyIndices[key];
|
||
if (index !== undefined) {
|
||
return this._arr[index].priority;
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Returns the key for the minimum element in this queue. If the queue is
|
||
* empty this function throws an Error. Takes `O(1)` time.
|
||
*/
|
||
PriorityQueue.prototype.min = function() {
|
||
if (this.size() === 0) {
|
||
throw new Error("Queue underflow");
|
||
}
|
||
return this._arr[0].key;
|
||
};
|
||
|
||
/**
|
||
* Inserts a new key into the priority queue. If the key already exists in
|
||
* the queue this function returns `false`; otherwise it will return `true`.
|
||
* Takes `O(n)` time.
|
||
*
|
||
* @param {Object} key the key to add
|
||
* @param {Number} priority the initial priority for the key
|
||
*/
|
||
PriorityQueue.prototype.add = function(key, priority) {
|
||
var keyIndices = this._keyIndices;
|
||
if (!(key in keyIndices)) {
|
||
var arr = this._arr;
|
||
var index = arr.length;
|
||
keyIndices[key] = index;
|
||
arr.push({key: key, priority: priority});
|
||
this._decrease(index);
|
||
return true;
|
||
}
|
||
return false;
|
||
};
|
||
|
||
/**
|
||
* Removes and returns the smallest key in the queue. Takes `O(log n)` time.
|
||
*/
|
||
PriorityQueue.prototype.removeMin = function() {
|
||
this._swap(0, this._arr.length - 1);
|
||
var min = this._arr.pop();
|
||
delete this._keyIndices[min.key];
|
||
this._heapify(0);
|
||
return min.key;
|
||
};
|
||
|
||
/**
|
||
* Decreases the priority for **key** to **priority**. If the new priority is
|
||
* greater than the previous priority, this function will throw an Error.
|
||
*
|
||
* @param {Object} key the key for which to raise priority
|
||
* @param {Number} priority the new priority for the key
|
||
*/
|
||
PriorityQueue.prototype.decrease = function(key, priority) {
|
||
var index = this._keyIndices[key];
|
||
if (priority > this._arr[index].priority) {
|
||
throw new Error("New priority is greater than current priority. " +
|
||
"Key: " + key + " Old: " + this._arr[index].priority + " New: " + priority);
|
||
}
|
||
this._arr[index].priority = priority;
|
||
this._decrease(index);
|
||
};
|
||
|
||
PriorityQueue.prototype._heapify = function(i) {
|
||
var arr = this._arr;
|
||
var l = 2 * i,
|
||
r = l + 1,
|
||
largest = i;
|
||
if (l < arr.length) {
|
||
largest = arr[l].priority < arr[largest].priority ? l : largest;
|
||
if (r < arr.length) {
|
||
largest = arr[r].priority < arr[largest].priority ? r : largest;
|
||
}
|
||
if (largest !== i) {
|
||
this._swap(i, largest);
|
||
this._heapify(largest);
|
||
}
|
||
}
|
||
};
|
||
|
||
PriorityQueue.prototype._decrease = function(index) {
|
||
var arr = this._arr;
|
||
var priority = arr[index].priority;
|
||
var parent;
|
||
while (index !== 0) {
|
||
parent = index >> 1;
|
||
if (arr[parent].priority < priority) {
|
||
break;
|
||
}
|
||
this._swap(index, parent);
|
||
index = parent;
|
||
}
|
||
};
|
||
|
||
PriorityQueue.prototype._swap = function(i, j) {
|
||
var arr = this._arr;
|
||
var keyIndices = this._keyIndices;
|
||
var origArrI = arr[i];
|
||
var origArrJ = arr[j];
|
||
arr[i] = origArrJ;
|
||
arr[j] = origArrI;
|
||
keyIndices[origArrJ.key] = i;
|
||
keyIndices[origArrI.key] = j;
|
||
};
|
||
|
||
},{}],7:[function(require,module,exports){
|
||
var util = require('./util');
|
||
|
||
module.exports = Set;
|
||
|
||
/**
|
||
* Constructs a new Set with an optional set of `initialKeys`.
|
||
*
|
||
* It is important to note that keys are coerced to String for most purposes
|
||
* with this object, similar to the behavior of JavaScript's Object. For
|
||
* example, the following will add only one key:
|
||
*
|
||
* var s = new Set();
|
||
* s.add(1);
|
||
* s.add("1");
|
||
*
|
||
* However, the type of the key is preserved internally so that `keys` returns
|
||
* the original key set uncoerced. For the above example, `keys` would return
|
||
* `[1]`.
|
||
*/
|
||
function Set(initialKeys) {
|
||
this._size = 0;
|
||
this._keys = {};
|
||
|
||
if (initialKeys) {
|
||
for (var i = 0, il = initialKeys.length; i < il; ++i) {
|
||
this.add(initialKeys[i]);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Returns a new Set that represents the set intersection of the array of given
|
||
* sets.
|
||
*/
|
||
Set.intersect = function(sets) {
|
||
if (sets.length === 0) {
|
||
return new Set();
|
||
}
|
||
|
||
var result = new Set(!util.isArray(sets[0]) ? sets[0].keys() : sets[0]);
|
||
for (var i = 1, il = sets.length; i < il; ++i) {
|
||
var resultKeys = result.keys(),
|
||
other = !util.isArray(sets[i]) ? sets[i] : new Set(sets[i]);
|
||
for (var j = 0, jl = resultKeys.length; j < jl; ++j) {
|
||
var key = resultKeys[j];
|
||
if (!other.has(key)) {
|
||
result.remove(key);
|
||
}
|
||
}
|
||
}
|
||
|
||
return result;
|
||
};
|
||
|
||
/**
|
||
* Returns a new Set that represents the set union of the array of given sets.
|
||
*/
|
||
Set.union = function(sets) {
|
||
var totalElems = util.reduce(sets, function(lhs, rhs) {
|
||
return lhs + (rhs.size ? rhs.size() : rhs.length);
|
||
}, 0);
|
||
var arr = new Array(totalElems);
|
||
|
||
var k = 0;
|
||
for (var i = 0, il = sets.length; i < il; ++i) {
|
||
var cur = sets[i],
|
||
keys = !util.isArray(cur) ? cur.keys() : cur;
|
||
for (var j = 0, jl = keys.length; j < jl; ++j) {
|
||
arr[k++] = keys[j];
|
||
}
|
||
}
|
||
|
||
return new Set(arr);
|
||
};
|
||
|
||
/**
|
||
* Returns the size of this set in `O(1)` time.
|
||
*/
|
||
Set.prototype.size = function() {
|
||
return this._size;
|
||
};
|
||
|
||
/**
|
||
* Returns the keys in this set. Takes `O(n)` time.
|
||
*/
|
||
Set.prototype.keys = function() {
|
||
return values(this._keys);
|
||
};
|
||
|
||
/**
|
||
* Tests if a key is present in this Set. Returns `true` if it is and `false`
|
||
* if not. Takes `O(1)` time.
|
||
*/
|
||
Set.prototype.has = function(key) {
|
||
return key in this._keys;
|
||
};
|
||
|
||
/**
|
||
* Adds a new key to this Set if it is not already present. Returns `true` if
|
||
* the key was added and `false` if it was already present. Takes `O(1)` time.
|
||
*/
|
||
Set.prototype.add = function(key) {
|
||
if (!(key in this._keys)) {
|
||
this._keys[key] = key;
|
||
++this._size;
|
||
return true;
|
||
}
|
||
return false;
|
||
};
|
||
|
||
/**
|
||
* Removes a key from this Set. If the key was removed this function returns
|
||
* `true`. If not, it returns `false`. Takes `O(1)` time.
|
||
*/
|
||
Set.prototype.remove = function(key) {
|
||
if (key in this._keys) {
|
||
delete this._keys[key];
|
||
--this._size;
|
||
return true;
|
||
}
|
||
return false;
|
||
};
|
||
|
||
/*
|
||
* Returns an array of all values for properties of **o**.
|
||
*/
|
||
function values(o) {
|
||
var ks = Object.keys(o),
|
||
len = ks.length,
|
||
result = new Array(len),
|
||
i;
|
||
for (i = 0; i < len; ++i) {
|
||
result[i] = o[ks[i]];
|
||
}
|
||
return result;
|
||
}
|
||
|
||
},{"./util":8}],8:[function(require,module,exports){
|
||
/*
|
||
* This polyfill comes from
|
||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
|
||
*/
|
||
if(!Array.isArray) {
|
||
exports.isArray = function (vArg) {
|
||
return Object.prototype.toString.call(vArg) === '[object Array]';
|
||
};
|
||
} else {
|
||
exports.isArray = Array.isArray;
|
||
}
|
||
|
||
/*
|
||
* Slightly adapted polyfill from
|
||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
|
||
*/
|
||
if ('function' !== typeof Array.prototype.reduce) {
|
||
exports.reduce = function(array, callback, opt_initialValue) {
|
||
'use strict';
|
||
if (null === array || 'undefined' === typeof array) {
|
||
// At the moment all modern browsers, that support strict mode, have
|
||
// native implementation of Array.prototype.reduce. For instance, IE8
|
||
// does not support strict mode, so this check is actually useless.
|
||
throw new TypeError(
|
||
'Array.prototype.reduce called on null or undefined');
|
||
}
|
||
if ('function' !== typeof callback) {
|
||
throw new TypeError(callback + ' is not a function');
|
||
}
|
||
var index, value,
|
||
length = array.length >>> 0,
|
||
isValueSet = false;
|
||
if (1 < arguments.length) {
|
||
value = opt_initialValue;
|
||
isValueSet = true;
|
||
}
|
||
for (index = 0; length > index; ++index) {
|
||
if (array.hasOwnProperty(index)) {
|
||
if (isValueSet) {
|
||
value = callback(value, array[index], index, array);
|
||
}
|
||
else {
|
||
value = array[index];
|
||
isValueSet = true;
|
||
}
|
||
}
|
||
}
|
||
if (!isValueSet) {
|
||
throw new TypeError('Reduce of empty array with no initial value');
|
||
}
|
||
return value;
|
||
};
|
||
} else {
|
||
exports.reduce = function(array, callback, opt_initialValue) {
|
||
return array.reduce(callback, opt_initialValue);
|
||
};
|
||
}
|
||
|
||
},{}],9:[function(require,module,exports){
|
||
module.exports = '1.1.3';
|
||
|
||
},{}],10:[function(require,module,exports){
|
||
require("./d3");
|
||
module.exports = d3;
|
||
(function () { delete this.d3; })(); // unset global
|
||
|
||
},{}],11:[function(require,module,exports){
|
||
/*
|
||
Copyright (c) 2012-2013 Chris Pettitt
|
||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
of this software and associated documentation files (the "Software"), to deal
|
||
in the Software without restriction, including without limitation the rights
|
||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
copies of the Software, and to permit persons to whom the Software is
|
||
furnished to do so, subject to the following conditions:
|
||
|
||
The above copyright notice and this permission notice shall be included in
|
||
all copies or substantial portions of the Software.
|
||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
THE SOFTWARE.
|
||
*/
|
||
exports.Digraph = require("graphlib").Digraph;
|
||
exports.Graph = require("graphlib").Graph;
|
||
exports.layout = require("./lib/layout");
|
||
exports.version = require("./lib/version");
|
||
|
||
},{"./lib/layout":12,"./lib/version":27,"graphlib":28}],12:[function(require,module,exports){
|
||
var util = require('./util'),
|
||
rank = require('./rank'),
|
||
order = require('./order'),
|
||
CGraph = require('graphlib').CGraph,
|
||
CDigraph = require('graphlib').CDigraph;
|
||
|
||
module.exports = function() {
|
||
// External configuration
|
||
var config = {
|
||
// How much debug information to include?
|
||
debugLevel: 0,
|
||
// Max number of sweeps to perform in order phase
|
||
orderMaxSweeps: order.DEFAULT_MAX_SWEEPS,
|
||
// Use network simplex algorithm in ranking
|
||
rankSimplex: false,
|
||
// Rank direction. Valid values are (TB, LR)
|
||
rankDir: 'TB'
|
||
};
|
||
|
||
// Phase functions
|
||
var position = require('./position')();
|
||
|
||
// This layout object
|
||
var self = {};
|
||
|
||
self.orderIters = util.propertyAccessor(self, config, 'orderMaxSweeps');
|
||
|
||
self.rankSimplex = util.propertyAccessor(self, config, 'rankSimplex');
|
||
|
||
self.nodeSep = delegateProperty(position.nodeSep);
|
||
self.edgeSep = delegateProperty(position.edgeSep);
|
||
self.universalSep = delegateProperty(position.universalSep);
|
||
self.rankSep = delegateProperty(position.rankSep);
|
||
self.rankDir = util.propertyAccessor(self, config, 'rankDir');
|
||
self.debugAlignment = delegateProperty(position.debugAlignment);
|
||
|
||
self.debugLevel = util.propertyAccessor(self, config, 'debugLevel', function(x) {
|
||
util.log.level = x;
|
||
position.debugLevel(x);
|
||
});
|
||
|
||
self.run = util.time('Total layout', run);
|
||
|
||
self._normalize = normalize;
|
||
|
||
return self;
|
||
|
||
/*
|
||
* Constructs an adjacency graph using the nodes and edges specified through
|
||
* config. For each node and edge we add a property `dagre` that contains an
|
||
* object that will hold intermediate and final layout information. Some of
|
||
* the contents include:
|
||
*
|
||
* 1) A generated ID that uniquely identifies the object.
|
||
* 2) Dimension information for nodes (copied from the source node).
|
||
* 3) Optional dimension information for edges.
|
||
*
|
||
* After the adjacency graph is constructed the code no longer needs to use
|
||
* the original nodes and edges passed in via config.
|
||
*/
|
||
function initLayoutGraph(inputGraph) {
|
||
var g = new CDigraph();
|
||
|
||
inputGraph.eachNode(function(u, value) {
|
||
if (value === undefined) value = {};
|
||
g.addNode(u, {
|
||
width: value.width,
|
||
height: value.height
|
||
});
|
||
if (value.hasOwnProperty('rank')) {
|
||
g.node(u).prefRank = value.rank;
|
||
}
|
||
});
|
||
|
||
// Set up subgraphs
|
||
if (inputGraph.parent) {
|
||
inputGraph.nodes().forEach(function(u) {
|
||
g.parent(u, inputGraph.parent(u));
|
||
});
|
||
}
|
||
|
||
inputGraph.eachEdge(function(e, u, v, value) {
|
||
if (value === undefined) value = {};
|
||
var newValue = {
|
||
e: e,
|
||
minLen: value.minLen || 1,
|
||
width: value.width || 0,
|
||
height: value.height || 0,
|
||
points: []
|
||
};
|
||
|
||
g.addEdge(null, u, v, newValue);
|
||
});
|
||
|
||
// Initial graph attributes
|
||
var graphValue = inputGraph.graph() || {};
|
||
g.graph({
|
||
rankDir: graphValue.rankDir || config.rankDir,
|
||
orderRestarts: graphValue.orderRestarts
|
||
});
|
||
|
||
return g;
|
||
}
|
||
|
||
function run(inputGraph) {
|
||
var rankSep = self.rankSep();
|
||
var g;
|
||
try {
|
||
// Build internal graph
|
||
g = util.time('initLayoutGraph', initLayoutGraph)(inputGraph);
|
||
|
||
if (g.order() === 0) {
|
||
return g;
|
||
}
|
||
|
||
// Make space for edge labels
|
||
g.eachEdge(function(e, s, t, a) {
|
||
a.minLen *= 2;
|
||
});
|
||
self.rankSep(rankSep / 2);
|
||
|
||
// Determine the rank for each node. Nodes with a lower rank will appear
|
||
// above nodes of higher rank.
|
||
util.time('rank.run', rank.run)(g, config.rankSimplex);
|
||
|
||
// Normalize the graph by ensuring that every edge is proper (each edge has
|
||
// a length of 1). We achieve this by adding dummy nodes to long edges,
|
||
// thus shortening them.
|
||
util.time('normalize', normalize)(g);
|
||
|
||
// Order the nodes so that edge crossings are minimized.
|
||
util.time('order', order)(g, config.orderMaxSweeps);
|
||
|
||
// Find the x and y coordinates for every node in the graph.
|
||
util.time('position', position.run)(g);
|
||
|
||
// De-normalize the graph by removing dummy nodes and augmenting the
|
||
// original long edges with coordinate information.
|
||
util.time('undoNormalize', undoNormalize)(g);
|
||
|
||
// Reverses points for edges that are in a reversed state.
|
||
util.time('fixupEdgePoints', fixupEdgePoints)(g);
|
||
|
||
// Restore delete edges and reverse edges that were reversed in the rank
|
||
// phase.
|
||
util.time('rank.restoreEdges', rank.restoreEdges)(g);
|
||
|
||
// Construct final result graph and return it
|
||
return util.time('createFinalGraph', createFinalGraph)(g, inputGraph.isDirected());
|
||
} finally {
|
||
self.rankSep(rankSep);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* This function is responsible for 'normalizing' the graph. The process of
|
||
* normalization ensures that no edge in the graph has spans more than one
|
||
* rank. To do this it inserts dummy nodes as needed and links them by adding
|
||
* dummy edges. This function keeps enough information in the dummy nodes and
|
||
* edges to ensure that the original graph can be reconstructed later.
|
||
*
|
||
* This method assumes that the input graph is cycle free.
|
||
*/
|
||
function normalize(g) {
|
||
var dummyCount = 0;
|
||
g.eachEdge(function(e, s, t, a) {
|
||
var sourceRank = g.node(s).rank;
|
||
var targetRank = g.node(t).rank;
|
||
if (sourceRank + 1 < targetRank) {
|
||
for (var u = s, rank = sourceRank + 1, i = 0; rank < targetRank; ++rank, ++i) {
|
||
var v = '_D' + (++dummyCount);
|
||
var node = {
|
||
width: a.width,
|
||
height: a.height,
|
||
edge: { id: e, source: s, target: t, attrs: a },
|
||
rank: rank,
|
||
dummy: true
|
||
};
|
||
|
||
// If this node represents a bend then we will use it as a control
|
||
// point. For edges with 2 segments this will be the center dummy
|
||
// node. For edges with more than two segments, this will be the
|
||
// first and last dummy node.
|
||
if (i === 0) node.index = 0;
|
||
else if (rank + 1 === targetRank) node.index = 1;
|
||
|
||
g.addNode(v, node);
|
||
g.addEdge(null, u, v, {});
|
||
u = v;
|
||
}
|
||
g.addEdge(null, u, t, {});
|
||
g.delEdge(e);
|
||
}
|
||
});
|
||
}
|
||
|
||
/*
|
||
* Reconstructs the graph as it was before normalization. The positions of
|
||
* dummy nodes are used to build an array of points for the original 'long'
|
||
* edge. Dummy nodes and edges are removed.
|
||
*/
|
||
function undoNormalize(g) {
|
||
g.eachNode(function(u, a) {
|
||
if (a.dummy) {
|
||
if ('index' in a) {
|
||
var edge = a.edge;
|
||
if (!g.hasEdge(edge.id)) {
|
||
g.addEdge(edge.id, edge.source, edge.target, edge.attrs);
|
||
}
|
||
var points = g.edge(edge.id).points;
|
||
points[a.index] = { x: a.x, y: a.y, ul: a.ul, ur: a.ur, dl: a.dl, dr: a.dr };
|
||
}
|
||
g.delNode(u);
|
||
}
|
||
});
|
||
}
|
||
|
||
/*
|
||
* For each edge that was reversed during the `acyclic` step, reverse its
|
||
* array of points.
|
||
*/
|
||
function fixupEdgePoints(g) {
|
||
g.eachEdge(function(e, s, t, a) { if (a.reversed) a.points.reverse(); });
|
||
}
|
||
|
||
function createFinalGraph(g, isDirected) {
|
||
var out = isDirected ? new CDigraph() : new CGraph();
|
||
out.graph(g.graph());
|
||
g.eachNode(function(u, value) { out.addNode(u, value); });
|
||
g.eachNode(function(u) { out.parent(u, g.parent(u)); });
|
||
g.eachEdge(function(e, u, v, value) {
|
||
out.addEdge(value.e, u, v, value);
|
||
});
|
||
|
||
// Attach bounding box information
|
||
var maxX = 0, maxY = 0;
|
||
g.eachNode(function(u, value) {
|
||
if (!g.children(u).length) {
|
||
maxX = Math.max(maxX, value.x + value.width / 2);
|
||
maxY = Math.max(maxY, value.y + value.height / 2);
|
||
}
|
||
});
|
||
g.eachEdge(function(e, u, v, value) {
|
||
var maxXPoints = Math.max.apply(Math, value.points.map(function(p) { return p.x; }));
|
||
var maxYPoints = Math.max.apply(Math, value.points.map(function(p) { return p.y; }));
|
||
maxX = Math.max(maxX, maxXPoints + value.width / 2);
|
||
maxY = Math.max(maxY, maxYPoints + value.height / 2);
|
||
});
|
||
out.graph().width = maxX;
|
||
out.graph().height = maxY;
|
||
|
||
return out;
|
||
}
|
||
|
||
/*
|
||
* Given a function, a new function is returned that invokes the given
|
||
* function. The return value from the function is always the `self` object.
|
||
*/
|
||
function delegateProperty(f) {
|
||
return function() {
|
||
if (!arguments.length) return f();
|
||
f.apply(null, arguments);
|
||
return self;
|
||
};
|
||
}
|
||
};
|
||
|
||
|
||
},{"./order":13,"./position":18,"./rank":19,"./util":26,"graphlib":28}],13:[function(require,module,exports){
|
||
var util = require('./util'),
|
||
crossCount = require('./order/crossCount'),
|
||
initLayerGraphs = require('./order/initLayerGraphs'),
|
||
initOrder = require('./order/initOrder'),
|
||
sortLayer = require('./order/sortLayer');
|
||
|
||
module.exports = order;
|
||
|
||
// The maximum number of sweeps to perform before finishing the order phase.
|
||
var DEFAULT_MAX_SWEEPS = 24;
|
||
order.DEFAULT_MAX_SWEEPS = DEFAULT_MAX_SWEEPS;
|
||
|
||
/*
|
||
* Runs the order phase with the specified `graph, `maxSweeps`, and
|
||
* `debugLevel`. If `maxSweeps` is not specified we use `DEFAULT_MAX_SWEEPS`.
|
||
* If `debugLevel` is not set we assume 0.
|
||
*/
|
||
function order(g, maxSweeps) {
|
||
if (arguments.length < 2) {
|
||
maxSweeps = DEFAULT_MAX_SWEEPS;
|
||
}
|
||
|
||
var restarts = g.graph().orderRestarts || 0;
|
||
|
||
var layerGraphs = initLayerGraphs(g);
|
||
// TODO: remove this when we add back support for ordering clusters
|
||
layerGraphs.forEach(function(lg) {
|
||
lg = lg.filterNodes(function(u) { return !g.children(u).length; });
|
||
});
|
||
|
||
var iters = 0,
|
||
currentBestCC,
|
||
allTimeBestCC = Number.MAX_VALUE,
|
||
allTimeBest = {};
|
||
|
||
function saveAllTimeBest() {
|
||
g.eachNode(function(u, value) { allTimeBest[u] = value.order; });
|
||
}
|
||
|
||
for (var j = 0; j < Number(restarts) + 1 && allTimeBestCC !== 0; ++j) {
|
||
currentBestCC = Number.MAX_VALUE;
|
||
initOrder(g, restarts > 0);
|
||
|
||
util.log(2, 'Order phase start cross count: ' + g.graph().orderInitCC);
|
||
|
||
var i, lastBest, cc;
|
||
for (i = 0, lastBest = 0; lastBest < 4 && i < maxSweeps && currentBestCC > 0; ++i, ++lastBest, ++iters) {
|
||
sweep(g, layerGraphs, i);
|
||
cc = crossCount(g);
|
||
if (cc < currentBestCC) {
|
||
lastBest = 0;
|
||
currentBestCC = cc;
|
||
if (cc < allTimeBestCC) {
|
||
saveAllTimeBest();
|
||
allTimeBestCC = cc;
|
||
}
|
||
}
|
||
util.log(3, 'Order phase start ' + j + ' iter ' + i + ' cross count: ' + cc);
|
||
}
|
||
}
|
||
|
||
Object.keys(allTimeBest).forEach(function(u) {
|
||
if (!g.children || !g.children(u).length) {
|
||
g.node(u).order = allTimeBest[u];
|
||
}
|
||
});
|
||
g.graph().orderCC = allTimeBestCC;
|
||
|
||
util.log(2, 'Order iterations: ' + iters);
|
||
util.log(2, 'Order phase best cross count: ' + g.graph().orderCC);
|
||
}
|
||
|
||
function predecessorWeights(g, nodes) {
|
||
var weights = {};
|
||
nodes.forEach(function(u) {
|
||
weights[u] = g.inEdges(u).map(function(e) {
|
||
return g.node(g.source(e)).order;
|
||
});
|
||
});
|
||
return weights;
|
||
}
|
||
|
||
function successorWeights(g, nodes) {
|
||
var weights = {};
|
||
nodes.forEach(function(u) {
|
||
weights[u] = g.outEdges(u).map(function(e) {
|
||
return g.node(g.target(e)).order;
|
||
});
|
||
});
|
||
return weights;
|
||
}
|
||
|
||
function sweep(g, layerGraphs, iter) {
|
||
if (iter % 2 === 0) {
|
||
sweepDown(g, layerGraphs, iter);
|
||
} else {
|
||
sweepUp(g, layerGraphs, iter);
|
||
}
|
||
}
|
||
|
||
function sweepDown(g, layerGraphs) {
|
||
var cg;
|
||
for (i = 1; i < layerGraphs.length; ++i) {
|
||
cg = sortLayer(layerGraphs[i], cg, predecessorWeights(g, layerGraphs[i].nodes()));
|
||
}
|
||
}
|
||
|
||
function sweepUp(g, layerGraphs) {
|
||
var cg;
|
||
for (i = layerGraphs.length - 2; i >= 0; --i) {
|
||
sortLayer(layerGraphs[i], cg, successorWeights(g, layerGraphs[i].nodes()));
|
||
}
|
||
}
|
||
|
||
},{"./order/crossCount":14,"./order/initLayerGraphs":15,"./order/initOrder":16,"./order/sortLayer":17,"./util":26}],14:[function(require,module,exports){
|
||
var util = require('../util');
|
||
|
||
module.exports = crossCount;
|
||
|
||
/*
|
||
* Returns the cross count for the given graph.
|
||
*/
|
||
function crossCount(g) {
|
||
var cc = 0;
|
||
var ordering = util.ordering(g);
|
||
for (var i = 1; i < ordering.length; ++i) {
|
||
cc += twoLayerCrossCount(g, ordering[i-1], ordering[i]);
|
||
}
|
||
return cc;
|
||
}
|
||
|
||
/*
|
||
* This function searches through a ranked and ordered graph and counts the
|
||
* number of edges that cross. This algorithm is derived from:
|
||
*
|
||
* W. Barth et al., Bilayer Cross Counting, JGAA, 8(2) 179–194 (2004)
|
||
*/
|
||
function twoLayerCrossCount(g, layer1, layer2) {
|
||
var indices = [];
|
||
layer1.forEach(function(u) {
|
||
var nodeIndices = [];
|
||
g.outEdges(u).forEach(function(e) { nodeIndices.push(g.node(g.target(e)).order); });
|
||
nodeIndices.sort(function(x, y) { return x - y; });
|
||
indices = indices.concat(nodeIndices);
|
||
});
|
||
|
||
var firstIndex = 1;
|
||
while (firstIndex < layer2.length) firstIndex <<= 1;
|
||
|
||
var treeSize = 2 * firstIndex - 1;
|
||
firstIndex -= 1;
|
||
|
||
var tree = [];
|
||
for (var i = 0; i < treeSize; ++i) { tree[i] = 0; }
|
||
|
||
var cc = 0;
|
||
indices.forEach(function(i) {
|
||
var treeIndex = i + firstIndex;
|
||
++tree[treeIndex];
|
||
while (treeIndex > 0) {
|
||
if (treeIndex % 2) {
|
||
cc += tree[treeIndex + 1];
|
||
}
|
||
treeIndex = (treeIndex - 1) >> 1;
|
||
++tree[treeIndex];
|
||
}
|
||
});
|
||
|
||
return cc;
|
||
}
|
||
|
||
},{"../util":26}],15:[function(require,module,exports){
|
||
var nodesFromList = require('graphlib').filter.nodesFromList,
|
||
/* jshint -W079 */
|
||
Set = require('cp-data').Set;
|
||
|
||
module.exports = initLayerGraphs;
|
||
|
||
/*
|
||
* This function takes a compound layered graph, g, and produces an array of
|
||
* layer graphs. Each entry in the array represents a subgraph of nodes
|
||
* relevant for performing crossing reduction on that layer.
|
||
*/
|
||
function initLayerGraphs(g) {
|
||
var ranks = [];
|
||
|
||
function dfs(u) {
|
||
if (u === null) {
|
||
g.children(u).forEach(function(v) { dfs(v); });
|
||
return;
|
||
}
|
||
|
||
var value = g.node(u);
|
||
value.minRank = ('rank' in value) ? value.rank : Number.MAX_VALUE;
|
||
value.maxRank = ('rank' in value) ? value.rank : Number.MIN_VALUE;
|
||
var uRanks = new Set();
|
||
g.children(u).forEach(function(v) {
|
||
var rs = dfs(v);
|
||
uRanks = Set.union([uRanks, rs]);
|
||
value.minRank = Math.min(value.minRank, g.node(v).minRank);
|
||
value.maxRank = Math.max(value.maxRank, g.node(v).maxRank);
|
||
});
|
||
|
||
if ('rank' in value) uRanks.add(value.rank);
|
||
|
||
uRanks.keys().forEach(function(r) {
|
||
if (!(r in ranks)) ranks[r] = [];
|
||
ranks[r].push(u);
|
||
});
|
||
|
||
return uRanks;
|
||
}
|
||
dfs(null);
|
||
|
||
var layerGraphs = [];
|
||
ranks.forEach(function(us, rank) {
|
||
layerGraphs[rank] = g.filterNodes(nodesFromList(us));
|
||
});
|
||
|
||
return layerGraphs;
|
||
}
|
||
|
||
},{"cp-data":5,"graphlib":28}],16:[function(require,module,exports){
|
||
var crossCount = require('./crossCount'),
|
||
util = require('../util');
|
||
|
||
module.exports = initOrder;
|
||
|
||
/*
|
||
* Given a graph with a set of layered nodes (i.e. nodes that have a `rank`
|
||
* attribute) this function attaches an `order` attribute that uniquely
|
||
* arranges each node of each rank. If no constraint graph is provided the
|
||
* order of the nodes in each rank is entirely arbitrary.
|
||
*/
|
||
function initOrder(g, random) {
|
||
var layers = [];
|
||
|
||
g.eachNode(function(u, value) {
|
||
var layer = layers[value.rank];
|
||
if (g.children && g.children(u).length > 0) return;
|
||
if (!layer) {
|
||
layer = layers[value.rank] = [];
|
||
}
|
||
layer.push(u);
|
||
});
|
||
|
||
layers.forEach(function(layer) {
|
||
if (random) {
|
||
util.shuffle(layer);
|
||
}
|
||
layer.forEach(function(u, i) {
|
||
g.node(u).order = i;
|
||
});
|
||
});
|
||
|
||
var cc = crossCount(g);
|
||
g.graph().orderInitCC = cc;
|
||
g.graph().orderCC = Number.MAX_VALUE;
|
||
}
|
||
|
||
},{"../util":26,"./crossCount":14}],17:[function(require,module,exports){
|
||
var util = require('../util');
|
||
/*
|
||
Digraph = require('graphlib').Digraph,
|
||
topsort = require('graphlib').alg.topsort,
|
||
nodesFromList = require('graphlib').filter.nodesFromList;
|
||
*/
|
||
|
||
module.exports = sortLayer;
|
||
|
||
/*
|
||
function sortLayer(g, cg, weights) {
|
||
var result = sortLayerSubgraph(g, null, cg, weights);
|
||
result.list.forEach(function(u, i) {
|
||
g.node(u).order = i;
|
||
});
|
||
return result.constraintGraph;
|
||
}
|
||
*/
|
||
|
||
function sortLayer(g, cg, weights) {
|
||
var ordering = [];
|
||
var bs = {};
|
||
g.eachNode(function(u, value) {
|
||
ordering[value.order] = u;
|
||
var ws = weights[u];
|
||
if (ws.length) {
|
||
bs[u] = util.sum(ws) / ws.length;
|
||
}
|
||
});
|
||
|
||
var toSort = g.nodes().filter(function(u) { return bs[u] !== undefined; });
|
||
toSort.sort(function(x, y) {
|
||
return bs[x] - bs[y] || g.node(x).order - g.node(y).order;
|
||
});
|
||
|
||
for (var i = 0, j = 0, jl = toSort.length; j < jl; ++i) {
|
||
if (bs[ordering[i]] !== undefined) {
|
||
g.node(toSort[j++]).order = i;
|
||
}
|
||
}
|
||
}
|
||
|
||
// TOOD: re-enable constrained sorting once we have a strategy for handling
|
||
// undefined barycenters.
|
||
/*
|
||
function sortLayerSubgraph(g, sg, cg, weights) {
|
||
cg = cg ? cg.filterNodes(nodesFromList(g.children(sg))) : new Digraph();
|
||
|
||
var nodeData = {};
|
||
g.children(sg).forEach(function(u) {
|
||
if (g.children(u).length) {
|
||
nodeData[u] = sortLayerSubgraph(g, u, cg, weights);
|
||
nodeData[u].firstSG = u;
|
||
nodeData[u].lastSG = u;
|
||
} else {
|
||
var ws = weights[u];
|
||
nodeData[u] = {
|
||
degree: ws.length,
|
||
barycenter: ws.length > 0 ? util.sum(ws) / ws.length : 0,
|
||
list: [u]
|
||
};
|
||
}
|
||
});
|
||
|
||
resolveViolatedConstraints(g, cg, nodeData);
|
||
|
||
var keys = Object.keys(nodeData);
|
||
keys.sort(function(x, y) {
|
||
return nodeData[x].barycenter - nodeData[y].barycenter;
|
||
});
|
||
|
||
var result = keys.map(function(u) { return nodeData[u]; })
|
||
.reduce(function(lhs, rhs) { return mergeNodeData(g, lhs, rhs); });
|
||
return result;
|
||
}
|
||
|
||
/*
|
||
function mergeNodeData(g, lhs, rhs) {
|
||
var cg = mergeDigraphs(lhs.constraintGraph, rhs.constraintGraph);
|
||
|
||
if (lhs.lastSG !== undefined && rhs.firstSG !== undefined) {
|
||
if (cg === undefined) {
|
||
cg = new Digraph();
|
||
}
|
||
if (!cg.hasNode(lhs.lastSG)) { cg.addNode(lhs.lastSG); }
|
||
cg.addNode(rhs.firstSG);
|
||
cg.addEdge(null, lhs.lastSG, rhs.firstSG);
|
||
}
|
||
|
||
return {
|
||
degree: lhs.degree + rhs.degree,
|
||
barycenter: (lhs.barycenter * lhs.degree + rhs.barycenter * rhs.degree) /
|
||
(lhs.degree + rhs.degree),
|
||
list: lhs.list.concat(rhs.list),
|
||
firstSG: lhs.firstSG !== undefined ? lhs.firstSG : rhs.firstSG,
|
||
lastSG: rhs.lastSG !== undefined ? rhs.lastSG : lhs.lastSG,
|
||
constraintGraph: cg
|
||
};
|
||
}
|
||
|
||
function mergeDigraphs(lhs, rhs) {
|
||
if (lhs === undefined) return rhs;
|
||
if (rhs === undefined) return lhs;
|
||
|
||
lhs = lhs.copy();
|
||
rhs.nodes().forEach(function(u) { lhs.addNode(u); });
|
||
rhs.edges().forEach(function(e, u, v) { lhs.addEdge(null, u, v); });
|
||
return lhs;
|
||
}
|
||
|
||
function resolveViolatedConstraints(g, cg, nodeData) {
|
||
// Removes nodes `u` and `v` from `cg` and makes any edges incident on them
|
||
// incident on `w` instead.
|
||
function collapseNodes(u, v, w) {
|
||
// TODO original paper removes self loops, but it is not obvious when this would happen
|
||
cg.inEdges(u).forEach(function(e) {
|
||
cg.delEdge(e);
|
||
cg.addEdge(null, cg.source(e), w);
|
||
});
|
||
|
||
cg.outEdges(v).forEach(function(e) {
|
||
cg.delEdge(e);
|
||
cg.addEdge(null, w, cg.target(e));
|
||
});
|
||
|
||
cg.delNode(u);
|
||
cg.delNode(v);
|
||
}
|
||
|
||
var violated;
|
||
while ((violated = findViolatedConstraint(cg, nodeData)) !== undefined) {
|
||
var source = cg.source(violated),
|
||
target = cg.target(violated);
|
||
|
||
var v;
|
||
while ((v = cg.addNode(null)) && g.hasNode(v)) {
|
||
cg.delNode(v);
|
||
}
|
||
|
||
// Collapse barycenter and list
|
||
nodeData[v] = mergeNodeData(g, nodeData[source], nodeData[target]);
|
||
delete nodeData[source];
|
||
delete nodeData[target];
|
||
|
||
collapseNodes(source, target, v);
|
||
if (cg.incidentEdges(v).length === 0) { cg.delNode(v); }
|
||
}
|
||
}
|
||
|
||
function findViolatedConstraint(cg, nodeData) {
|
||
var us = topsort(cg);
|
||
for (var i = 0; i < us.length; ++i) {
|
||
var u = us[i];
|
||
var inEdges = cg.inEdges(u);
|
||
for (var j = 0; j < inEdges.length; ++j) {
|
||
var e = inEdges[j];
|
||
if (nodeData[cg.source(e)].barycenter >= nodeData[u].barycenter) {
|
||
return e;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
*/
|
||
|
||
},{"../util":26}],18:[function(require,module,exports){
|
||
var util = require('./util');
|
||
|
||
/*
|
||
* The algorithms here are based on Brandes and Köpf, "Fast and Simple
|
||
* Horizontal Coordinate Assignment".
|
||
*/
|
||
module.exports = function() {
|
||
// External configuration
|
||
var config = {
|
||
nodeSep: 50,
|
||
edgeSep: 10,
|
||
universalSep: null,
|
||
rankSep: 30
|
||
};
|
||
|
||
var self = {};
|
||
|
||
self.nodeSep = util.propertyAccessor(self, config, 'nodeSep');
|
||
self.edgeSep = util.propertyAccessor(self, config, 'edgeSep');
|
||
// If not null this separation value is used for all nodes and edges
|
||
// regardless of their widths. `nodeSep` and `edgeSep` are ignored with this
|
||
// option.
|
||
self.universalSep = util.propertyAccessor(self, config, 'universalSep');
|
||
self.rankSep = util.propertyAccessor(self, config, 'rankSep');
|
||
self.debugLevel = util.propertyAccessor(self, config, 'debugLevel');
|
||
|
||
self.run = run;
|
||
|
||
return self;
|
||
|
||
function run(g) {
|
||
g = g.filterNodes(util.filterNonSubgraphs(g));
|
||
|
||
var layering = util.ordering(g);
|
||
|
||
var conflicts = findConflicts(g, layering);
|
||
|
||
var xss = {};
|
||
['u', 'd'].forEach(function(vertDir) {
|
||
if (vertDir === 'd') layering.reverse();
|
||
|
||
['l', 'r'].forEach(function(horizDir) {
|
||
if (horizDir === 'r') reverseInnerOrder(layering);
|
||
|
||
var dir = vertDir + horizDir;
|
||
var align = verticalAlignment(g, layering, conflicts, vertDir === 'u' ? 'predecessors' : 'successors');
|
||
xss[dir]= horizontalCompaction(g, layering, align.pos, align.root, align.align);
|
||
|
||
if (config.debugLevel >= 3)
|
||
debugPositioning(vertDir + horizDir, g, layering, xss[dir]);
|
||
|
||
if (horizDir === 'r') flipHorizontally(xss[dir]);
|
||
|
||
if (horizDir === 'r') reverseInnerOrder(layering);
|
||
});
|
||
|
||
if (vertDir === 'd') layering.reverse();
|
||
});
|
||
|
||
balance(g, layering, xss);
|
||
|
||
g.eachNode(function(v) {
|
||
var xs = [];
|
||
for (var alignment in xss) {
|
||
var alignmentX = xss[alignment][v];
|
||
posXDebug(alignment, g, v, alignmentX);
|
||
xs.push(alignmentX);
|
||
}
|
||
xs.sort(function(x, y) { return x - y; });
|
||
posX(g, v, (xs[1] + xs[2]) / 2);
|
||
});
|
||
|
||
// Align y coordinates with ranks
|
||
var y = 0, reverseY = g.graph().rankDir === 'BT' || g.graph().rankDir === 'RL';
|
||
layering.forEach(function(layer) {
|
||
var maxHeight = util.max(layer.map(function(u) { return height(g, u); }));
|
||
y += maxHeight / 2;
|
||
layer.forEach(function(u) {
|
||
posY(g, u, reverseY ? -y : y);
|
||
});
|
||
y += maxHeight / 2 + config.rankSep;
|
||
});
|
||
|
||
// Translate layout so that top left corner of bounding rectangle has
|
||
// coordinate (0, 0).
|
||
var minX = util.min(g.nodes().map(function(u) { return posX(g, u) - width(g, u) / 2; }));
|
||
var minY = util.min(g.nodes().map(function(u) { return posY(g, u) - height(g, u) / 2; }));
|
||
g.eachNode(function(u) {
|
||
posX(g, u, posX(g, u) - minX);
|
||
posY(g, u, posY(g, u) - minY);
|
||
});
|
||
}
|
||
|
||
/*
|
||
* Generate an ID that can be used to represent any undirected edge that is
|
||
* incident on `u` and `v`.
|
||
*/
|
||
function undirEdgeId(u, v) {
|
||
return u < v
|
||
? u.toString().length + ':' + u + '-' + v
|
||
: v.toString().length + ':' + v + '-' + u;
|
||
}
|
||
|
||
function findConflicts(g, layering) {
|
||
var conflicts = {}, // Set of conflicting edge ids
|
||
pos = {}, // Position of node in its layer
|
||
prevLayer,
|
||
currLayer,
|
||
k0, // Position of the last inner segment in the previous layer
|
||
l, // Current position in the current layer (for iteration up to `l1`)
|
||
k1; // Position of the next inner segment in the previous layer or
|
||
// the position of the last element in the previous layer
|
||
|
||
if (layering.length <= 2) return conflicts;
|
||
|
||
function updateConflicts(v) {
|
||
var k = pos[v];
|
||
if (k < k0 || k > k1) {
|
||
conflicts[undirEdgeId(currLayer[l], v)] = true;
|
||
}
|
||
}
|
||
|
||
layering[1].forEach(function(u, i) { pos[u] = i; });
|
||
for (var i = 1; i < layering.length - 1; ++i) {
|
||
prevLayer = layering[i];
|
||
currLayer = layering[i+1];
|
||
k0 = 0;
|
||
l = 0;
|
||
|
||
// Scan current layer for next node that is incident to an inner segement
|
||
// between layering[i+1] and layering[i].
|
||
for (var l1 = 0; l1 < currLayer.length; ++l1) {
|
||
var u = currLayer[l1]; // Next inner segment in the current layer or
|
||
// last node in the current layer
|
||
pos[u] = l1;
|
||
k1 = undefined;
|
||
|
||
if (g.node(u).dummy) {
|
||
var uPred = g.predecessors(u)[0];
|
||
// Note: In the case of self loops and sideways edges it is possible
|
||
// for a dummy not to have a predecessor.
|
||
if (uPred !== undefined && g.node(uPred).dummy)
|
||
k1 = pos[uPred];
|
||
}
|
||
if (k1 === undefined && l1 === currLayer.length - 1)
|
||
k1 = prevLayer.length - 1;
|
||
|
||
if (k1 !== undefined) {
|
||
for (; l <= l1; ++l) {
|
||
g.predecessors(currLayer[l]).forEach(updateConflicts);
|
||
}
|
||
k0 = k1;
|
||
}
|
||
}
|
||
}
|
||
|
||
return conflicts;
|
||
}
|
||
|
||
function verticalAlignment(g, layering, conflicts, relationship) {
|
||
var pos = {}, // Position for a node in its layer
|
||
root = {}, // Root of the block that the node participates in
|
||
align = {}; // Points to the next node in the block or, if the last
|
||
// element in the block, points to the first block's root
|
||
|
||
layering.forEach(function(layer) {
|
||
layer.forEach(function(u, i) {
|
||
root[u] = u;
|
||
align[u] = u;
|
||
pos[u] = i;
|
||
});
|
||
});
|
||
|
||
layering.forEach(function(layer) {
|
||
var prevIdx = -1;
|
||
layer.forEach(function(v) {
|
||
var related = g[relationship](v), // Adjacent nodes from the previous layer
|
||
mid; // The mid point in the related array
|
||
|
||
if (related.length > 0) {
|
||
related.sort(function(x, y) { return pos[x] - pos[y]; });
|
||
mid = (related.length - 1) / 2;
|
||
related.slice(Math.floor(mid), Math.ceil(mid) + 1).forEach(function(u) {
|
||
if (align[v] === v) {
|
||
if (!conflicts[undirEdgeId(u, v)] && prevIdx < pos[u]) {
|
||
align[u] = v;
|
||
align[v] = root[v] = root[u];
|
||
prevIdx = pos[u];
|
||
}
|
||
}
|
||
});
|
||
}
|
||
});
|
||
});
|
||
|
||
return { pos: pos, root: root, align: align };
|
||
}
|
||
|
||
// This function deviates from the standard BK algorithm in two ways. First
|
||
// it takes into account the size of the nodes. Second it includes a fix to
|
||
// the original algorithm that is described in Carstens, "Node and Label
|
||
// Placement in a Layered Layout Algorithm".
|
||
function horizontalCompaction(g, layering, pos, root, align) {
|
||
var sink = {}, // Mapping of node id -> sink node id for class
|
||
maybeShift = {}, // Mapping of sink node id -> { class node id, min shift }
|
||
shift = {}, // Mapping of sink node id -> shift
|
||
pred = {}, // Mapping of node id -> predecessor node (or null)
|
||
xs = {}; // Calculated X positions
|
||
|
||
layering.forEach(function(layer) {
|
||
layer.forEach(function(u, i) {
|
||
sink[u] = u;
|
||
maybeShift[u] = {};
|
||
if (i > 0)
|
||
pred[u] = layer[i - 1];
|
||
});
|
||
});
|
||
|
||
function updateShift(toShift, neighbor, delta) {
|
||
if (!(neighbor in maybeShift[toShift])) {
|
||
maybeShift[toShift][neighbor] = delta;
|
||
} else {
|
||
maybeShift[toShift][neighbor] = Math.min(maybeShift[toShift][neighbor], delta);
|
||
}
|
||
}
|
||
|
||
function placeBlock(v) {
|
||
if (!(v in xs)) {
|
||
xs[v] = 0;
|
||
var w = v;
|
||
do {
|
||
if (pos[w] > 0) {
|
||
var u = root[pred[w]];
|
||
placeBlock(u);
|
||
if (sink[v] === v) {
|
||
sink[v] = sink[u];
|
||
}
|
||
var delta = sep(g, pred[w]) + sep(g, w);
|
||
if (sink[v] !== sink[u]) {
|
||
updateShift(sink[u], sink[v], xs[v] - xs[u] - delta);
|
||
} else {
|
||
xs[v] = Math.max(xs[v], xs[u] + delta);
|
||
}
|
||
}
|
||
w = align[w];
|
||
} while (w !== v);
|
||
}
|
||
}
|
||
|
||
// Root coordinates relative to sink
|
||
util.values(root).forEach(function(v) {
|
||
placeBlock(v);
|
||
});
|
||
|
||
// Absolute coordinates
|
||
// There is an assumption here that we've resolved shifts for any classes
|
||
// that begin at an earlier layer. We guarantee this by visiting layers in
|
||
// order.
|
||
layering.forEach(function(layer) {
|
||
layer.forEach(function(v) {
|
||
xs[v] = xs[root[v]];
|
||
if (v === root[v] && v === sink[v]) {
|
||
var minShift = 0;
|
||
if (v in maybeShift && Object.keys(maybeShift[v]).length > 0) {
|
||
minShift = util.min(Object.keys(maybeShift[v])
|
||
.map(function(u) {
|
||
return maybeShift[v][u] + (u in shift ? shift[u] : 0);
|
||
}
|
||
));
|
||
}
|
||
shift[v] = minShift;
|
||
}
|
||
});
|
||
});
|
||
|
||
layering.forEach(function(layer) {
|
||
layer.forEach(function(v) {
|
||
xs[v] += shift[sink[root[v]]] || 0;
|
||
});
|
||
});
|
||
|
||
return xs;
|
||
}
|
||
|
||
function findMinCoord(g, layering, xs) {
|
||
return util.min(layering.map(function(layer) {
|
||
var u = layer[0];
|
||
return xs[u];
|
||
}));
|
||
}
|
||
|
||
function findMaxCoord(g, layering, xs) {
|
||
return util.max(layering.map(function(layer) {
|
||
var u = layer[layer.length - 1];
|
||
return xs[u];
|
||
}));
|
||
}
|
||
|
||
function balance(g, layering, xss) {
|
||
var min = {}, // Min coordinate for the alignment
|
||
max = {}, // Max coordinate for the alginment
|
||
smallestAlignment,
|
||
shift = {}; // Amount to shift a given alignment
|
||
|
||
function updateAlignment(v) {
|
||
xss[alignment][v] += shift[alignment];
|
||
}
|
||
|
||
var smallest = Number.POSITIVE_INFINITY;
|
||
for (var alignment in xss) {
|
||
var xs = xss[alignment];
|
||
min[alignment] = findMinCoord(g, layering, xs);
|
||
max[alignment] = findMaxCoord(g, layering, xs);
|
||
var w = max[alignment] - min[alignment];
|
||
if (w < smallest) {
|
||
smallest = w;
|
||
smallestAlignment = alignment;
|
||
}
|
||
}
|
||
|
||
// Determine how much to adjust positioning for each alignment
|
||
['u', 'd'].forEach(function(vertDir) {
|
||
['l', 'r'].forEach(function(horizDir) {
|
||
var alignment = vertDir + horizDir;
|
||
shift[alignment] = horizDir === 'l'
|
||
? min[smallestAlignment] - min[alignment]
|
||
: max[smallestAlignment] - max[alignment];
|
||
});
|
||
});
|
||
|
||
// Find average of medians for xss array
|
||
for (alignment in xss) {
|
||
g.eachNode(updateAlignment);
|
||
}
|
||
}
|
||
|
||
function flipHorizontally(xs) {
|
||
for (var u in xs) {
|
||
xs[u] = -xs[u];
|
||
}
|
||
}
|
||
|
||
function reverseInnerOrder(layering) {
|
||
layering.forEach(function(layer) {
|
||
layer.reverse();
|
||
});
|
||
}
|
||
|
||
function width(g, u) {
|
||
switch (g.graph().rankDir) {
|
||
case 'LR': return g.node(u).height;
|
||
case 'RL': return g.node(u).height;
|
||
default: return g.node(u).width;
|
||
}
|
||
}
|
||
|
||
function height(g, u) {
|
||
switch(g.graph().rankDir) {
|
||
case 'LR': return g.node(u).width;
|
||
case 'RL': return g.node(u).width;
|
||
default: return g.node(u).height;
|
||
}
|
||
}
|
||
|
||
function sep(g, u) {
|
||
if (config.universalSep !== null) {
|
||
return config.universalSep;
|
||
}
|
||
var w = width(g, u);
|
||
var s = g.node(u).dummy ? config.edgeSep : config.nodeSep;
|
||
return (w + s) / 2;
|
||
}
|
||
|
||
function posX(g, u, x) {
|
||
if (g.graph().rankDir === 'LR' || g.graph().rankDir === 'RL') {
|
||
if (arguments.length < 3) {
|
||
return g.node(u).y;
|
||
} else {
|
||
g.node(u).y = x;
|
||
}
|
||
} else {
|
||
if (arguments.length < 3) {
|
||
return g.node(u).x;
|
||
} else {
|
||
g.node(u).x = x;
|
||
}
|
||
}
|
||
}
|
||
|
||
function posXDebug(name, g, u, x) {
|
||
if (g.graph().rankDir === 'LR' || g.graph().rankDir === 'RL') {
|
||
if (arguments.length < 3) {
|
||
return g.node(u)[name];
|
||
} else {
|
||
g.node(u)[name] = x;
|
||
}
|
||
} else {
|
||
if (arguments.length < 3) {
|
||
return g.node(u)[name];
|
||
} else {
|
||
g.node(u)[name] = x;
|
||
}
|
||
}
|
||
}
|
||
|
||
function posY(g, u, y) {
|
||
if (g.graph().rankDir === 'LR' || g.graph().rankDir === 'RL') {
|
||
if (arguments.length < 3) {
|
||
return g.node(u).x;
|
||
} else {
|
||
g.node(u).x = y;
|
||
}
|
||
} else {
|
||
if (arguments.length < 3) {
|
||
return g.node(u).y;
|
||
} else {
|
||
g.node(u).y = y;
|
||
}
|
||
}
|
||
}
|
||
|
||
function debugPositioning(align, g, layering, xs) {
|
||
layering.forEach(function(l, li) {
|
||
var u, xU;
|
||
l.forEach(function(v) {
|
||
var xV = xs[v];
|
||
if (u) {
|
||
var s = sep(g, u) + sep(g, v);
|
||
if (xV - xU < s)
|
||
console.log('Position phase: sep violation. Align: ' + align + '. Layer: ' + li + '. ' +
|
||
'U: ' + u + ' V: ' + v + '. Actual sep: ' + (xV - xU) + ' Expected sep: ' + s);
|
||
}
|
||
u = v;
|
||
xU = xV;
|
||
});
|
||
});
|
||
}
|
||
};
|
||
|
||
},{"./util":26}],19:[function(require,module,exports){
|
||
var util = require('./util'),
|
||
acyclic = require('./rank/acyclic'),
|
||
initRank = require('./rank/initRank'),
|
||
feasibleTree = require('./rank/feasibleTree'),
|
||
constraints = require('./rank/constraints'),
|
||
simplex = require('./rank/simplex'),
|
||
components = require('graphlib').alg.components,
|
||
filter = require('graphlib').filter;
|
||
|
||
exports.run = run;
|
||
exports.restoreEdges = restoreEdges;
|
||
|
||
/*
|
||
* Heuristic function that assigns a rank to each node of the input graph with
|
||
* the intent of minimizing edge lengths, while respecting the `minLen`
|
||
* attribute of incident edges.
|
||
*
|
||
* Prerequisites:
|
||
*
|
||
* * Each edge in the input graph must have an assigned 'minLen' attribute
|
||
*/
|
||
function run(g, useSimplex) {
|
||
expandSelfLoops(g);
|
||
|
||
// If there are rank constraints on nodes, then build a new graph that
|
||
// encodes the constraints.
|
||
util.time('constraints.apply', constraints.apply)(g);
|
||
|
||
expandSidewaysEdges(g);
|
||
|
||
// Reverse edges to get an acyclic graph, we keep the graph in an acyclic
|
||
// state until the very end.
|
||
util.time('acyclic', acyclic)(g);
|
||
|
||
// Convert the graph into a flat graph for ranking
|
||
var flatGraph = g.filterNodes(util.filterNonSubgraphs(g));
|
||
|
||
// Assign an initial ranking using DFS.
|
||
initRank(flatGraph);
|
||
|
||
// For each component improve the assigned ranks.
|
||
components(flatGraph).forEach(function(cmpt) {
|
||
var subgraph = flatGraph.filterNodes(filter.nodesFromList(cmpt));
|
||
rankComponent(subgraph, useSimplex);
|
||
});
|
||
|
||
// Relax original constraints
|
||
util.time('constraints.relax', constraints.relax(g));
|
||
|
||
// When handling nodes with constrained ranks it is possible to end up with
|
||
// edges that point to previous ranks. Most of the subsequent algorithms assume
|
||
// that edges are pointing to successive ranks only. Here we reverse any "back
|
||
// edges" and mark them as such. The acyclic algorithm will reverse them as a
|
||
// post processing step.
|
||
util.time('reorientEdges', reorientEdges)(g);
|
||
}
|
||
|
||
function restoreEdges(g) {
|
||
acyclic.undo(g);
|
||
}
|
||
|
||
/*
|
||
* Expand self loops into three dummy nodes. One will sit above the incident
|
||
* node, one will be at the same level, and one below. The result looks like:
|
||
*
|
||
* /--<--x--->--\
|
||
* node y
|
||
* \--<--z--->--/
|
||
*
|
||
* Dummy nodes x, y, z give us the shape of a loop and node y is where we place
|
||
* the label.
|
||
*
|
||
* TODO: consolidate knowledge of dummy node construction.
|
||
* TODO: support minLen = 2
|
||
*/
|
||
function expandSelfLoops(g) {
|
||
g.eachEdge(function(e, u, v, a) {
|
||
if (u === v) {
|
||
var x = addDummyNode(g, e, u, v, a, 0, false),
|
||
y = addDummyNode(g, e, u, v, a, 1, true),
|
||
z = addDummyNode(g, e, u, v, a, 2, false);
|
||
g.addEdge(null, x, u, {minLen: 1, selfLoop: true});
|
||
g.addEdge(null, x, y, {minLen: 1, selfLoop: true});
|
||
g.addEdge(null, u, z, {minLen: 1, selfLoop: true});
|
||
g.addEdge(null, y, z, {minLen: 1, selfLoop: true});
|
||
g.delEdge(e);
|
||
}
|
||
});
|
||
}
|
||
|
||
function expandSidewaysEdges(g) {
|
||
g.eachEdge(function(e, u, v, a) {
|
||
if (u === v) {
|
||
var origEdge = a.originalEdge,
|
||
dummy = addDummyNode(g, origEdge.e, origEdge.u, origEdge.v, origEdge.value, 0, true);
|
||
g.addEdge(null, u, dummy, {minLen: 1});
|
||
g.addEdge(null, dummy, v, {minLen: 1});
|
||
g.delEdge(e);
|
||
}
|
||
});
|
||
}
|
||
|
||
function addDummyNode(g, e, u, v, a, index, isLabel) {
|
||
return g.addNode(null, {
|
||
width: isLabel ? a.width : 0,
|
||
height: isLabel ? a.height : 0,
|
||
edge: { id: e, source: u, target: v, attrs: a },
|
||
dummy: true,
|
||
index: index
|
||
});
|
||
}
|
||
|
||
function reorientEdges(g) {
|
||
g.eachEdge(function(e, u, v, value) {
|
||
if (g.node(u).rank > g.node(v).rank) {
|
||
g.delEdge(e);
|
||
value.reversed = true;
|
||
g.addEdge(e, v, u, value);
|
||
}
|
||
});
|
||
}
|
||
|
||
function rankComponent(subgraph, useSimplex) {
|
||
var spanningTree = feasibleTree(subgraph);
|
||
|
||
if (useSimplex) {
|
||
util.log(1, 'Using network simplex for ranking');
|
||
simplex(subgraph, spanningTree);
|
||
}
|
||
normalize(subgraph);
|
||
}
|
||
|
||
function normalize(g) {
|
||
var m = util.min(g.nodes().map(function(u) { return g.node(u).rank; }));
|
||
g.eachNode(function(u, node) { node.rank -= m; });
|
||
}
|
||
|
||
},{"./rank/acyclic":20,"./rank/constraints":21,"./rank/feasibleTree":22,"./rank/initRank":23,"./rank/simplex":25,"./util":26,"graphlib":28}],20:[function(require,module,exports){
|
||
var util = require('../util');
|
||
|
||
module.exports = acyclic;
|
||
module.exports.undo = undo;
|
||
|
||
/*
|
||
* This function takes a directed graph that may have cycles and reverses edges
|
||
* as appropriate to break these cycles. Each reversed edge is assigned a
|
||
* `reversed` attribute with the value `true`.
|
||
*
|
||
* There should be no self loops in the graph.
|
||
*/
|
||
function acyclic(g) {
|
||
var onStack = {},
|
||
visited = {},
|
||
reverseCount = 0;
|
||
|
||
function dfs(u) {
|
||
if (u in visited) return;
|
||
visited[u] = onStack[u] = true;
|
||
g.outEdges(u).forEach(function(e) {
|
||
var t = g.target(e),
|
||
value;
|
||
|
||
if (u === t) {
|
||
console.error('Warning: found self loop "' + e + '" for node "' + u + '"');
|
||
} else if (t in onStack) {
|
||
value = g.edge(e);
|
||
g.delEdge(e);
|
||
value.reversed = true;
|
||
++reverseCount;
|
||
g.addEdge(e, t, u, value);
|
||
} else {
|
||
dfs(t);
|
||
}
|
||
});
|
||
|
||
delete onStack[u];
|
||
}
|
||
|
||
g.eachNode(function(u) { dfs(u); });
|
||
|
||
util.log(2, 'Acyclic Phase: reversed ' + reverseCount + ' edge(s)');
|
||
|
||
return reverseCount;
|
||
}
|
||
|
||
/*
|
||
* Given a graph that has had the acyclic operation applied, this function
|
||
* undoes that operation. More specifically, any edge with the `reversed`
|
||
* attribute is again reversed to restore the original direction of the edge.
|
||
*/
|
||
function undo(g) {
|
||
g.eachEdge(function(e, s, t, a) {
|
||
if (a.reversed) {
|
||
delete a.reversed;
|
||
g.delEdge(e);
|
||
g.addEdge(e, t, s, a);
|
||
}
|
||
});
|
||
}
|
||
|
||
},{"../util":26}],21:[function(require,module,exports){
|
||
exports.apply = function(g) {
|
||
function dfs(sg) {
|
||
var rankSets = {};
|
||
g.children(sg).forEach(function(u) {
|
||
if (g.children(u).length) {
|
||
dfs(u);
|
||
return;
|
||
}
|
||
|
||
var value = g.node(u),
|
||
prefRank = value.prefRank;
|
||
if (prefRank !== undefined) {
|
||
if (!checkSupportedPrefRank(prefRank)) { return; }
|
||
|
||
if (!(prefRank in rankSets)) {
|
||
rankSets.prefRank = [u];
|
||
} else {
|
||
rankSets.prefRank.push(u);
|
||
}
|
||
|
||
var newU = rankSets[prefRank];
|
||
if (newU === undefined) {
|
||
newU = rankSets[prefRank] = g.addNode(null, { originalNodes: [] });
|
||
g.parent(newU, sg);
|
||
}
|
||
|
||
redirectInEdges(g, u, newU, prefRank === 'min');
|
||
redirectOutEdges(g, u, newU, prefRank === 'max');
|
||
|
||
// Save original node and remove it from reduced graph
|
||
g.node(newU).originalNodes.push({ u: u, value: value, parent: sg });
|
||
g.delNode(u);
|
||
}
|
||
});
|
||
|
||
addLightEdgesFromMinNode(g, sg, rankSets.min);
|
||
addLightEdgesToMaxNode(g, sg, rankSets.max);
|
||
}
|
||
|
||
dfs(null);
|
||
};
|
||
|
||
function checkSupportedPrefRank(prefRank) {
|
||
if (prefRank !== 'min' && prefRank !== 'max' && prefRank.indexOf('same_') !== 0) {
|
||
console.error('Unsupported rank type: ' + prefRank);
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
function redirectInEdges(g, u, newU, reverse) {
|
||
g.inEdges(u).forEach(function(e) {
|
||
var origValue = g.edge(e),
|
||
value;
|
||
if (origValue.originalEdge) {
|
||
value = origValue;
|
||
} else {
|
||
value = {
|
||
originalEdge: { e: e, u: g.source(e), v: g.target(e), value: origValue },
|
||
minLen: g.edge(e).minLen
|
||
};
|
||
}
|
||
|
||
// Do not reverse edges for self-loops.
|
||
if (origValue.selfLoop) {
|
||
reverse = false;
|
||
}
|
||
|
||
if (reverse) {
|
||
// Ensure that all edges to min are reversed
|
||
g.addEdge(null, newU, g.source(e), value);
|
||
value.reversed = true;
|
||
} else {
|
||
g.addEdge(null, g.source(e), newU, value);
|
||
}
|
||
});
|
||
}
|
||
|
||
function redirectOutEdges(g, u, newU, reverse) {
|
||
g.outEdges(u).forEach(function(e) {
|
||
var origValue = g.edge(e),
|
||
value;
|
||
if (origValue.originalEdge) {
|
||
value = origValue;
|
||
} else {
|
||
value = {
|
||
originalEdge: { e: e, u: g.source(e), v: g.target(e), value: origValue },
|
||
minLen: g.edge(e).minLen
|
||
};
|
||
}
|
||
|
||
// Do not reverse edges for self-loops.
|
||
if (origValue.selfLoop) {
|
||
reverse = false;
|
||
}
|
||
|
||
if (reverse) {
|
||
// Ensure that all edges from max are reversed
|
||
g.addEdge(null, g.target(e), newU, value);
|
||
value.reversed = true;
|
||
} else {
|
||
g.addEdge(null, newU, g.target(e), value);
|
||
}
|
||
});
|
||
}
|
||
|
||
function addLightEdgesFromMinNode(g, sg, minNode) {
|
||
if (minNode !== undefined) {
|
||
g.children(sg).forEach(function(u) {
|
||
// The dummy check ensures we don't add an edge if the node is involved
|
||
// in a self loop or sideways edge.
|
||
if (u !== minNode && !g.outEdges(minNode, u).length && !g.node(u).dummy) {
|
||
g.addEdge(null, minNode, u, { minLen: 0 });
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
function addLightEdgesToMaxNode(g, sg, maxNode) {
|
||
if (maxNode !== undefined) {
|
||
g.children(sg).forEach(function(u) {
|
||
// The dummy check ensures we don't add an edge if the node is involved
|
||
// in a self loop or sideways edge.
|
||
if (u !== maxNode && !g.outEdges(u, maxNode).length && !g.node(u).dummy) {
|
||
g.addEdge(null, u, maxNode, { minLen: 0 });
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
/*
|
||
* This function "relaxes" the constraints applied previously by the "apply"
|
||
* function. It expands any nodes that were collapsed and assigns the rank of
|
||
* the collapsed node to each of the expanded nodes. It also restores the
|
||
* original edges and removes any dummy edges pointing at the collapsed nodes.
|
||
*
|
||
* Note that the process of removing collapsed nodes also removes dummy edges
|
||
* automatically.
|
||
*/
|
||
exports.relax = function(g) {
|
||
// Save original edges
|
||
var originalEdges = [];
|
||
g.eachEdge(function(e, u, v, value) {
|
||
var originalEdge = value.originalEdge;
|
||
if (originalEdge) {
|
||
originalEdges.push(originalEdge);
|
||
}
|
||
});
|
||
|
||
// Expand collapsed nodes
|
||
g.eachNode(function(u, value) {
|
||
var originalNodes = value.originalNodes;
|
||
if (originalNodes) {
|
||
originalNodes.forEach(function(originalNode) {
|
||
originalNode.value.rank = value.rank;
|
||
g.addNode(originalNode.u, originalNode.value);
|
||
g.parent(originalNode.u, originalNode.parent);
|
||
});
|
||
g.delNode(u);
|
||
}
|
||
});
|
||
|
||
// Restore original edges
|
||
originalEdges.forEach(function(edge) {
|
||
g.addEdge(edge.e, edge.u, edge.v, edge.value);
|
||
});
|
||
};
|
||
|
||
},{}],22:[function(require,module,exports){
|
||
/* jshint -W079 */
|
||
var Set = require('cp-data').Set,
|
||
/* jshint +W079 */
|
||
Digraph = require('graphlib').Digraph,
|
||
util = require('../util');
|
||
|
||
module.exports = feasibleTree;
|
||
|
||
/*
|
||
* Given an acyclic graph with each node assigned a `rank` attribute, this
|
||
* function constructs and returns a spanning tree. This function may reduce
|
||
* the length of some edges from the initial rank assignment while maintaining
|
||
* the `minLen` specified by each edge.
|
||
*
|
||
* Prerequisites:
|
||
*
|
||
* * The input graph is acyclic
|
||
* * Each node in the input graph has an assigned `rank` attribute
|
||
* * Each edge in the input graph has an assigned `minLen` attribute
|
||
*
|
||
* Outputs:
|
||
*
|
||
* A feasible spanning tree for the input graph (i.e. a spanning tree that
|
||
* respects each graph edge's `minLen` attribute) represented as a Digraph with
|
||
* a `root` attribute on graph.
|
||
*
|
||
* Nodes have the same id and value as that in the input graph.
|
||
*
|
||
* Edges in the tree have arbitrarily assigned ids. The attributes for edges
|
||
* include `reversed`. `reversed` indicates that the edge is a
|
||
* back edge in the input graph.
|
||
*/
|
||
function feasibleTree(g) {
|
||
var remaining = new Set(g.nodes()),
|
||
tree = new Digraph();
|
||
|
||
if (remaining.size() === 1) {
|
||
var root = g.nodes()[0];
|
||
tree.addNode(root, {});
|
||
tree.graph({ root: root });
|
||
return tree;
|
||
}
|
||
|
||
function addTightEdges(v) {
|
||
var continueToScan = true;
|
||
g.predecessors(v).forEach(function(u) {
|
||
if (remaining.has(u) && !slack(g, u, v)) {
|
||
if (remaining.has(v)) {
|
||
tree.addNode(v, {});
|
||
remaining.remove(v);
|
||
tree.graph({ root: v });
|
||
}
|
||
|
||
tree.addNode(u, {});
|
||
tree.addEdge(null, u, v, { reversed: true });
|
||
remaining.remove(u);
|
||
addTightEdges(u);
|
||
continueToScan = false;
|
||
}
|
||
});
|
||
|
||
g.successors(v).forEach(function(w) {
|
||
if (remaining.has(w) && !slack(g, v, w)) {
|
||
if (remaining.has(v)) {
|
||
tree.addNode(v, {});
|
||
remaining.remove(v);
|
||
tree.graph({ root: v });
|
||
}
|
||
|
||
tree.addNode(w, {});
|
||
tree.addEdge(null, v, w, {});
|
||
remaining.remove(w);
|
||
addTightEdges(w);
|
||
continueToScan = false;
|
||
}
|
||
});
|
||
return continueToScan;
|
||
}
|
||
|
||
function createTightEdge() {
|
||
var minSlack = Number.MAX_VALUE;
|
||
remaining.keys().forEach(function(v) {
|
||
g.predecessors(v).forEach(function(u) {
|
||
if (!remaining.has(u)) {
|
||
var edgeSlack = slack(g, u, v);
|
||
if (Math.abs(edgeSlack) < Math.abs(minSlack)) {
|
||
minSlack = -edgeSlack;
|
||
}
|
||
}
|
||
});
|
||
|
||
g.successors(v).forEach(function(w) {
|
||
if (!remaining.has(w)) {
|
||
var edgeSlack = slack(g, v, w);
|
||
if (Math.abs(edgeSlack) < Math.abs(minSlack)) {
|
||
minSlack = edgeSlack;
|
||
}
|
||
}
|
||
});
|
||
});
|
||
|
||
tree.eachNode(function(u) { g.node(u).rank -= minSlack; });
|
||
}
|
||
|
||
while (remaining.size()) {
|
||
var nodesToSearch = !tree.order() ? remaining.keys() : tree.nodes();
|
||
for (var i = 0, il = nodesToSearch.length;
|
||
i < il && addTightEdges(nodesToSearch[i]);
|
||
++i);
|
||
if (remaining.size()) {
|
||
createTightEdge();
|
||
}
|
||
}
|
||
|
||
return tree;
|
||
}
|
||
|
||
function slack(g, u, v) {
|
||
var rankDiff = g.node(v).rank - g.node(u).rank;
|
||
var maxMinLen = util.max(g.outEdges(u, v)
|
||
.map(function(e) { return g.edge(e).minLen; }));
|
||
return rankDiff - maxMinLen;
|
||
}
|
||
|
||
},{"../util":26,"cp-data":5,"graphlib":28}],23:[function(require,module,exports){
|
||
var util = require('../util'),
|
||
topsort = require('graphlib').alg.topsort;
|
||
|
||
module.exports = initRank;
|
||
|
||
/*
|
||
* Assigns a `rank` attribute to each node in the input graph and ensures that
|
||
* this rank respects the `minLen` attribute of incident edges.
|
||
*
|
||
* Prerequisites:
|
||
*
|
||
* * The input graph must be acyclic
|
||
* * Each edge in the input graph must have an assigned 'minLen' attribute
|
||
*/
|
||
function initRank(g) {
|
||
var sorted = topsort(g);
|
||
|
||
sorted.forEach(function(u) {
|
||
var inEdges = g.inEdges(u);
|
||
if (inEdges.length === 0) {
|
||
g.node(u).rank = 0;
|
||
return;
|
||
}
|
||
|
||
var minLens = inEdges.map(function(e) {
|
||
return g.node(g.source(e)).rank + g.edge(e).minLen;
|
||
});
|
||
g.node(u).rank = util.max(minLens);
|
||
});
|
||
}
|
||
|
||
},{"../util":26,"graphlib":28}],24:[function(require,module,exports){
|
||
module.exports = {
|
||
slack: slack
|
||
};
|
||
|
||
/*
|
||
* A helper to calculate the slack between two nodes (`u` and `v`) given a
|
||
* `minLen` constraint. The slack represents how much the distance between `u`
|
||
* and `v` could shrink while maintaining the `minLen` constraint. If the value
|
||
* is negative then the constraint is currently violated.
|
||
*
|
||
This function requires that `u` and `v` are in `graph` and they both have a
|
||
`rank` attribute.
|
||
*/
|
||
function slack(graph, u, v, minLen) {
|
||
return Math.abs(graph.node(u).rank - graph.node(v).rank) - minLen;
|
||
}
|
||
|
||
},{}],25:[function(require,module,exports){
|
||
var util = require('../util'),
|
||
rankUtil = require('./rankUtil');
|
||
|
||
module.exports = simplex;
|
||
|
||
function simplex(graph, spanningTree) {
|
||
// The network simplex algorithm repeatedly replaces edges of
|
||
// the spanning tree with negative cut values until no such
|
||
// edge exists.
|
||
initCutValues(graph, spanningTree);
|
||
while (true) {
|
||
var e = leaveEdge(spanningTree);
|
||
if (e === null) break;
|
||
var f = enterEdge(graph, spanningTree, e);
|
||
exchange(graph, spanningTree, e, f);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Set the cut values of edges in the spanning tree by a depth-first
|
||
* postorder traversal. The cut value corresponds to the cost, in
|
||
* terms of a ranking's edge length sum, of lengthening an edge.
|
||
* Negative cut values typically indicate edges that would yield a
|
||
* smaller edge length sum if they were lengthened.
|
||
*/
|
||
function initCutValues(graph, spanningTree) {
|
||
computeLowLim(spanningTree);
|
||
|
||
spanningTree.eachEdge(function(id, u, v, treeValue) {
|
||
treeValue.cutValue = 0;
|
||
});
|
||
|
||
// Propagate cut values up the tree.
|
||
function dfs(n) {
|
||
var children = spanningTree.successors(n);
|
||
for (var c in children) {
|
||
var child = children[c];
|
||
dfs(child);
|
||
}
|
||
if (n !== spanningTree.graph().root) {
|
||
setCutValue(graph, spanningTree, n);
|
||
}
|
||
}
|
||
dfs(spanningTree.graph().root);
|
||
}
|
||
|
||
/*
|
||
* Perform a DFS postorder traversal, labeling each node v with
|
||
* its traversal order 'lim(v)' and the minimum traversal number
|
||
* of any of its descendants 'low(v)'. This provides an efficient
|
||
* way to test whether u is an ancestor of v since
|
||
* low(u) <= lim(v) <= lim(u) if and only if u is an ancestor.
|
||
*/
|
||
function computeLowLim(tree) {
|
||
var postOrderNum = 0;
|
||
|
||
function dfs(n) {
|
||
var children = tree.successors(n);
|
||
var low = postOrderNum;
|
||
for (var c in children) {
|
||
var child = children[c];
|
||
dfs(child);
|
||
low = Math.min(low, tree.node(child).low);
|
||
}
|
||
tree.node(n).low = low;
|
||
tree.node(n).lim = postOrderNum++;
|
||
}
|
||
|
||
dfs(tree.graph().root);
|
||
}
|
||
|
||
/*
|
||
* To compute the cut value of the edge parent -> child, we consider
|
||
* it and any other graph edges to or from the child.
|
||
* parent
|
||
* |
|
||
* child
|
||
* / \
|
||
* u v
|
||
*/
|
||
function setCutValue(graph, tree, child) {
|
||
var parentEdge = tree.inEdges(child)[0];
|
||
|
||
// List of child's children in the spanning tree.
|
||
var grandchildren = [];
|
||
var grandchildEdges = tree.outEdges(child);
|
||
for (var gce in grandchildEdges) {
|
||
grandchildren.push(tree.target(grandchildEdges[gce]));
|
||
}
|
||
|
||
var cutValue = 0;
|
||
|
||
// TODO: Replace unit increment/decrement with edge weights.
|
||
var E = 0; // Edges from child to grandchild's subtree.
|
||
var F = 0; // Edges to child from grandchild's subtree.
|
||
var G = 0; // Edges from child to nodes outside of child's subtree.
|
||
var H = 0; // Edges from nodes outside of child's subtree to child.
|
||
|
||
// Consider all graph edges from child.
|
||
var outEdges = graph.outEdges(child);
|
||
var gc;
|
||
for (var oe in outEdges) {
|
||
var succ = graph.target(outEdges[oe]);
|
||
for (gc in grandchildren) {
|
||
if (inSubtree(tree, succ, grandchildren[gc])) {
|
||
E++;
|
||
}
|
||
}
|
||
if (!inSubtree(tree, succ, child)) {
|
||
G++;
|
||
}
|
||
}
|
||
|
||
// Consider all graph edges to child.
|
||
var inEdges = graph.inEdges(child);
|
||
for (var ie in inEdges) {
|
||
var pred = graph.source(inEdges[ie]);
|
||
for (gc in grandchildren) {
|
||
if (inSubtree(tree, pred, grandchildren[gc])) {
|
||
F++;
|
||
}
|
||
}
|
||
if (!inSubtree(tree, pred, child)) {
|
||
H++;
|
||
}
|
||
}
|
||
|
||
// Contributions depend on the alignment of the parent -> child edge
|
||
// and the child -> u or v edges.
|
||
var grandchildCutSum = 0;
|
||
for (gc in grandchildren) {
|
||
var cv = tree.edge(grandchildEdges[gc]).cutValue;
|
||
if (!tree.edge(grandchildEdges[gc]).reversed) {
|
||
grandchildCutSum += cv;
|
||
} else {
|
||
grandchildCutSum -= cv;
|
||
}
|
||
}
|
||
|
||
if (!tree.edge(parentEdge).reversed) {
|
||
cutValue += grandchildCutSum - E + F - G + H;
|
||
} else {
|
||
cutValue -= grandchildCutSum - E + F - G + H;
|
||
}
|
||
|
||
tree.edge(parentEdge).cutValue = cutValue;
|
||
}
|
||
|
||
/*
|
||
* Return whether n is a node in the subtree with the given
|
||
* root.
|
||
*/
|
||
function inSubtree(tree, n, root) {
|
||
return (tree.node(root).low <= tree.node(n).lim &&
|
||
tree.node(n).lim <= tree.node(root).lim);
|
||
}
|
||
|
||
/*
|
||
* Return an edge from the tree with a negative cut value, or null if there
|
||
* is none.
|
||
*/
|
||
function leaveEdge(tree) {
|
||
var edges = tree.edges();
|
||
for (var n in edges) {
|
||
var e = edges[n];
|
||
var treeValue = tree.edge(e);
|
||
if (treeValue.cutValue < 0) {
|
||
return e;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/*
|
||
* The edge e should be an edge in the tree, with an underlying edge
|
||
* in the graph, with a negative cut value. Of the two nodes incident
|
||
* on the edge, take the lower one. enterEdge returns an edge with
|
||
* minimum slack going from outside of that node's subtree to inside
|
||
* of that node's subtree.
|
||
*/
|
||
function enterEdge(graph, tree, e) {
|
||
var source = tree.source(e);
|
||
var target = tree.target(e);
|
||
var lower = tree.node(target).lim < tree.node(source).lim ? target : source;
|
||
|
||
// Is the tree edge aligned with the graph edge?
|
||
var aligned = !tree.edge(e).reversed;
|
||
|
||
var minSlack = Number.POSITIVE_INFINITY;
|
||
var minSlackEdge;
|
||
if (aligned) {
|
||
graph.eachEdge(function(id, u, v, value) {
|
||
if (id !== e && inSubtree(tree, u, lower) && !inSubtree(tree, v, lower)) {
|
||
var slack = rankUtil.slack(graph, u, v, value.minLen);
|
||
if (slack < minSlack) {
|
||
minSlack = slack;
|
||
minSlackEdge = id;
|
||
}
|
||
}
|
||
});
|
||
} else {
|
||
graph.eachEdge(function(id, u, v, value) {
|
||
if (id !== e && !inSubtree(tree, u, lower) && inSubtree(tree, v, lower)) {
|
||
var slack = rankUtil.slack(graph, u, v, value.minLen);
|
||
if (slack < minSlack) {
|
||
minSlack = slack;
|
||
minSlackEdge = id;
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
if (minSlackEdge === undefined) {
|
||
var outside = [];
|
||
var inside = [];
|
||
graph.eachNode(function(id) {
|
||
if (!inSubtree(tree, id, lower)) {
|
||
outside.push(id);
|
||
} else {
|
||
inside.push(id);
|
||
}
|
||
});
|
||
throw new Error('No edge found from outside of tree to inside');
|
||
}
|
||
|
||
return minSlackEdge;
|
||
}
|
||
|
||
/*
|
||
* Replace edge e with edge f in the tree, recalculating the tree root,
|
||
* the nodes' low and lim properties and the edges' cut values.
|
||
*/
|
||
function exchange(graph, tree, e, f) {
|
||
tree.delEdge(e);
|
||
var source = graph.source(f);
|
||
var target = graph.target(f);
|
||
|
||
// Redirect edges so that target is the root of its subtree.
|
||
function redirect(v) {
|
||
var edges = tree.inEdges(v);
|
||
for (var i in edges) {
|
||
var e = edges[i];
|
||
var u = tree.source(e);
|
||
var value = tree.edge(e);
|
||
redirect(u);
|
||
tree.delEdge(e);
|
||
value.reversed = !value.reversed;
|
||
tree.addEdge(e, v, u, value);
|
||
}
|
||
}
|
||
|
||
redirect(target);
|
||
|
||
var root = source;
|
||
var edges = tree.inEdges(root);
|
||
while (edges.length > 0) {
|
||
root = tree.source(edges[0]);
|
||
edges = tree.inEdges(root);
|
||
}
|
||
|
||
tree.graph().root = root;
|
||
|
||
tree.addEdge(null, source, target, {cutValue: 0});
|
||
|
||
initCutValues(graph, tree);
|
||
|
||
adjustRanks(graph, tree);
|
||
}
|
||
|
||
/*
|
||
* Reset the ranks of all nodes based on the current spanning tree.
|
||
* The rank of the tree's root remains unchanged, while all other
|
||
* nodes are set to the sum of minimum length constraints along
|
||
* the path from the root.
|
||
*/
|
||
function adjustRanks(graph, tree) {
|
||
function dfs(p) {
|
||
var children = tree.successors(p);
|
||
children.forEach(function(c) {
|
||
var minLen = minimumLength(graph, p, c);
|
||
graph.node(c).rank = graph.node(p).rank + minLen;
|
||
dfs(c);
|
||
});
|
||
}
|
||
|
||
dfs(tree.graph().root);
|
||
}
|
||
|
||
/*
|
||
* If u and v are connected by some edges in the graph, return the
|
||
* minimum length of those edges, as a positive number if v succeeds
|
||
* u and as a negative number if v precedes u.
|
||
*/
|
||
function minimumLength(graph, u, v) {
|
||
var outEdges = graph.outEdges(u, v);
|
||
if (outEdges.length > 0) {
|
||
return util.max(outEdges.map(function(e) {
|
||
return graph.edge(e).minLen;
|
||
}));
|
||
}
|
||
|
||
var inEdges = graph.inEdges(u, v);
|
||
if (inEdges.length > 0) {
|
||
return -util.max(inEdges.map(function(e) {
|
||
return graph.edge(e).minLen;
|
||
}));
|
||
}
|
||
}
|
||
|
||
},{"../util":26,"./rankUtil":24}],26:[function(require,module,exports){
|
||
/*
|
||
* Returns the smallest value in the array.
|
||
*/
|
||
exports.min = function(values) {
|
||
return Math.min.apply(Math, values);
|
||
};
|
||
|
||
/*
|
||
* Returns the largest value in the array.
|
||
*/
|
||
exports.max = function(values) {
|
||
return Math.max.apply(Math, values);
|
||
};
|
||
|
||
/*
|
||
* Returns `true` only if `f(x)` is `true` for all `x` in `xs`. Otherwise
|
||
* returns `false`. This function will return immediately if it finds a
|
||
* case where `f(x)` does not hold.
|
||
*/
|
||
exports.all = function(xs, f) {
|
||
for (var i = 0; i < xs.length; ++i) {
|
||
if (!f(xs[i])) {
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
};
|
||
|
||
/*
|
||
* Accumulates the sum of elements in the given array using the `+` operator.
|
||
*/
|
||
exports.sum = function(values) {
|
||
return values.reduce(function(acc, x) { return acc + x; }, 0);
|
||
};
|
||
|
||
/*
|
||
* Returns an array of all values in the given object.
|
||
*/
|
||
exports.values = function(obj) {
|
||
return Object.keys(obj).map(function(k) { return obj[k]; });
|
||
};
|
||
|
||
exports.shuffle = function(array) {
|
||
for (i = array.length - 1; i > 0; --i) {
|
||
var j = Math.floor(Math.random() * (i + 1));
|
||
var aj = array[j];
|
||
array[j] = array[i];
|
||
array[i] = aj;
|
||
}
|
||
};
|
||
|
||
exports.propertyAccessor = function(self, config, field, setHook) {
|
||
return function(x) {
|
||
if (!arguments.length) return config[field];
|
||
config[field] = x;
|
||
if (setHook) setHook(x);
|
||
return self;
|
||
};
|
||
};
|
||
|
||
/*
|
||
* Given a layered, directed graph with `rank` and `order` node attributes,
|
||
* this function returns an array of ordered ranks. Each rank contains an array
|
||
* of the ids of the nodes in that rank in the order specified by the `order`
|
||
* attribute.
|
||
*/
|
||
exports.ordering = function(g) {
|
||
var ordering = [];
|
||
g.eachNode(function(u, value) {
|
||
var rank = ordering[value.rank] || (ordering[value.rank] = []);
|
||
rank[value.order] = u;
|
||
});
|
||
return ordering;
|
||
};
|
||
|
||
/*
|
||
* A filter that can be used with `filterNodes` to get a graph that only
|
||
* includes nodes that do not contain others nodes.
|
||
*/
|
||
exports.filterNonSubgraphs = function(g) {
|
||
return function(u) {
|
||
return g.children(u).length === 0;
|
||
};
|
||
};
|
||
|
||
/*
|
||
* Returns a new function that wraps `func` with a timer. The wrapper logs the
|
||
* time it takes to execute the function.
|
||
*
|
||
* The timer will be enabled provided `log.level >= 1`.
|
||
*/
|
||
function time(name, func) {
|
||
return function() {
|
||
var start = new Date().getTime();
|
||
try {
|
||
return func.apply(null, arguments);
|
||
} finally {
|
||
log(1, name + ' time: ' + (new Date().getTime() - start) + 'ms');
|
||
}
|
||
};
|
||
}
|
||
time.enabled = false;
|
||
|
||
exports.time = time;
|
||
|
||
/*
|
||
* A global logger with the specification `log(level, message, ...)` that
|
||
* will log a message to the console if `log.level >= level`.
|
||
*/
|
||
function log(level) {
|
||
if (log.level >= level) {
|
||
console.log.apply(console, Array.prototype.slice.call(arguments, 1));
|
||
}
|
||
}
|
||
log.level = 0;
|
||
|
||
exports.log = log;
|
||
|
||
},{}],27:[function(require,module,exports){
|
||
module.exports = '0.4.5';
|
||
|
||
},{}],28:[function(require,module,exports){
|
||
exports.Graph = require("./lib/Graph");
|
||
exports.Digraph = require("./lib/Digraph");
|
||
exports.CGraph = require("./lib/CGraph");
|
||
exports.CDigraph = require("./lib/CDigraph");
|
||
require("./lib/graph-converters");
|
||
|
||
exports.alg = {
|
||
isAcyclic: require("./lib/alg/isAcyclic"),
|
||
components: require("./lib/alg/components"),
|
||
dijkstra: require("./lib/alg/dijkstra"),
|
||
dijkstraAll: require("./lib/alg/dijkstraAll"),
|
||
findCycles: require("./lib/alg/findCycles"),
|
||
floydWarshall: require("./lib/alg/floydWarshall"),
|
||
postorder: require("./lib/alg/postorder"),
|
||
preorder: require("./lib/alg/preorder"),
|
||
prim: require("./lib/alg/prim"),
|
||
tarjan: require("./lib/alg/tarjan"),
|
||
topsort: require("./lib/alg/topsort")
|
||
};
|
||
|
||
exports.converter = {
|
||
json: require("./lib/converter/json.js")
|
||
};
|
||
|
||
var filter = require("./lib/filter");
|
||
exports.filter = {
|
||
all: filter.all,
|
||
nodesFromList: filter.nodesFromList
|
||
};
|
||
|
||
exports.version = require("./lib/version");
|
||
|
||
},{"./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){
|
||
/* jshint -W079 */
|
||
var Set = require("cp-data").Set;
|
||
/* jshint +W079 */
|
||
|
||
module.exports = BaseGraph;
|
||
|
||
function BaseGraph() {
|
||
// The value assigned to the graph itself.
|
||
this._value = undefined;
|
||
|
||
// Map of node id -> { id, value }
|
||
this._nodes = {};
|
||
|
||
// Map of edge id -> { id, u, v, value }
|
||
this._edges = {};
|
||
|
||
// Used to generate a unique id in the graph
|
||
this._nextId = 0;
|
||
}
|
||
|
||
// Number of nodes
|
||
BaseGraph.prototype.order = function() {
|
||
return Object.keys(this._nodes).length;
|
||
};
|
||
|
||
// Number of edges
|
||
BaseGraph.prototype.size = function() {
|
||
return Object.keys(this._edges).length;
|
||
};
|
||
|
||
// Accessor for graph level value
|
||
BaseGraph.prototype.graph = function(value) {
|
||
if (arguments.length === 0) {
|
||
return this._value;
|
||
}
|
||
this._value = value;
|
||
};
|
||
|
||
BaseGraph.prototype.hasNode = function(u) {
|
||
return u in this._nodes;
|
||
};
|
||
|
||
BaseGraph.prototype.node = function(u, value) {
|
||
var node = this._strictGetNode(u);
|
||
if (arguments.length === 1) {
|
||
return node.value;
|
||
}
|
||
node.value = value;
|
||
};
|
||
|
||
BaseGraph.prototype.nodes = function() {
|
||
var nodes = [];
|
||
this.eachNode(function(id) { nodes.push(id); });
|
||
return nodes;
|
||
};
|
||
|
||
BaseGraph.prototype.eachNode = function(func) {
|
||
for (var k in this._nodes) {
|
||
var node = this._nodes[k];
|
||
func(node.id, node.value);
|
||
}
|
||
};
|
||
|
||
BaseGraph.prototype.hasEdge = function(e) {
|
||
return e in this._edges;
|
||
};
|
||
|
||
BaseGraph.prototype.edge = function(e, value) {
|
||
var edge = this._strictGetEdge(e);
|
||
if (arguments.length === 1) {
|
||
return edge.value;
|
||
}
|
||
edge.value = value;
|
||
};
|
||
|
||
BaseGraph.prototype.edges = function() {
|
||
var es = [];
|
||
this.eachEdge(function(id) { es.push(id); });
|
||
return es;
|
||
};
|
||
|
||
BaseGraph.prototype.eachEdge = function(func) {
|
||
for (var k in this._edges) {
|
||
var edge = this._edges[k];
|
||
func(edge.id, edge.u, edge.v, edge.value);
|
||
}
|
||
};
|
||
|
||
BaseGraph.prototype.incidentNodes = function(e) {
|
||
var edge = this._strictGetEdge(e);
|
||
return [edge.u, edge.v];
|
||
};
|
||
|
||
BaseGraph.prototype.addNode = function(u, value) {
|
||
if (u === undefined || u === null) {
|
||
do {
|
||
u = "_" + (++this._nextId);
|
||
} while (this.hasNode(u));
|
||
} else if (this.hasNode(u)) {
|
||
throw new Error("Graph already has node '" + u + "'");
|
||
}
|
||
this._nodes[u] = { id: u, value: value };
|
||
return u;
|
||
};
|
||
|
||
BaseGraph.prototype.delNode = function(u) {
|
||
this._strictGetNode(u);
|
||
this.incidentEdges(u).forEach(function(e) { this.delEdge(e); }, this);
|
||
delete this._nodes[u];
|
||
};
|
||
|
||
// inMap and outMap are opposite sides of an incidence map. For example, for
|
||
// Graph these would both come from the _incidentEdges map, while for Digraph
|
||
// they would come from _inEdges and _outEdges.
|
||
BaseGraph.prototype._addEdge = function(e, u, v, value, inMap, outMap) {
|
||
this._strictGetNode(u);
|
||
this._strictGetNode(v);
|
||
|
||
if (e === undefined || e === null) {
|
||
do {
|
||
e = "_" + (++this._nextId);
|
||
} while (this.hasEdge(e));
|
||
}
|
||
else if (this.hasEdge(e)) {
|
||
throw new Error("Graph already has edge '" + e + "'");
|
||
}
|
||
|
||
this._edges[e] = { id: e, u: u, v: v, value: value };
|
||
addEdgeToMap(inMap[v], u, e);
|
||
addEdgeToMap(outMap[u], v, e);
|
||
|
||
return e;
|
||
};
|
||
|
||
// See note for _addEdge regarding inMap and outMap.
|
||
BaseGraph.prototype._delEdge = function(e, inMap, outMap) {
|
||
var edge = this._strictGetEdge(e);
|
||
delEdgeFromMap(inMap[edge.v], edge.u, e);
|
||
delEdgeFromMap(outMap[edge.u], edge.v, e);
|
||
delete this._edges[e];
|
||
};
|
||
|
||
BaseGraph.prototype.copy = function() {
|
||
var copy = new this.constructor();
|
||
copy.graph(this.graph());
|
||
this.eachNode(function(u, value) { copy.addNode(u, value); });
|
||
this.eachEdge(function(e, u, v, value) { copy.addEdge(e, u, v, value); });
|
||
copy._nextId = this._nextId;
|
||
return copy;
|
||
};
|
||
|
||
BaseGraph.prototype.filterNodes = function(filter) {
|
||
var copy = new this.constructor();
|
||
copy.graph(this.graph());
|
||
this.eachNode(function(u, value) {
|
||
if (filter(u)) {
|
||
copy.addNode(u, value);
|
||
}
|
||
});
|
||
this.eachEdge(function(e, u, v, value) {
|
||
if (copy.hasNode(u) && copy.hasNode(v)) {
|
||
copy.addEdge(e, u, v, value);
|
||
}
|
||
});
|
||
return copy;
|
||
};
|
||
|
||
BaseGraph.prototype._strictGetNode = function(u) {
|
||
var node = this._nodes[u];
|
||
if (node === undefined) {
|
||
throw new Error("Node '" + u + "' is not in graph");
|
||
}
|
||
return node;
|
||
};
|
||
|
||
BaseGraph.prototype._strictGetEdge = function(e) {
|
||
var edge = this._edges[e];
|
||
if (edge === undefined) {
|
||
throw new Error("Edge '" + e + "' is not in graph");
|
||
}
|
||
return edge;
|
||
};
|
||
|
||
function addEdgeToMap(map, v, e) {
|
||
(map[v] || (map[v] = new Set())).add(e);
|
||
}
|
||
|
||
function delEdgeFromMap(map, v, e) {
|
||
var vEntry = map[v];
|
||
vEntry.remove(e);
|
||
if (vEntry.size() === 0) {
|
||
delete map[v];
|
||
}
|
||
}
|
||
|
||
|
||
},{"cp-data":5}],30:[function(require,module,exports){
|
||
var Digraph = require("./Digraph"),
|
||
compoundify = require("./compoundify");
|
||
|
||
var CDigraph = compoundify(Digraph);
|
||
|
||
module.exports = CDigraph;
|
||
|
||
CDigraph.fromDigraph = function(src) {
|
||
var g = new CDigraph(),
|
||
graphValue = src.graph();
|
||
|
||
if (graphValue !== undefined) {
|
||
g.graph(graphValue);
|
||
}
|
||
|
||
src.eachNode(function(u, value) {
|
||
if (value === undefined) {
|
||
g.addNode(u);
|
||
} else {
|
||
g.addNode(u, value);
|
||
}
|
||
});
|
||
src.eachEdge(function(e, u, v, value) {
|
||
if (value === undefined) {
|
||
g.addEdge(null, u, v);
|
||
} else {
|
||
g.addEdge(null, u, v, value);
|
||
}
|
||
});
|
||
return g;
|
||
};
|
||
|
||
CDigraph.prototype.toString = function() {
|
||
return "CDigraph " + JSON.stringify(this, null, 2);
|
||
};
|
||
|
||
},{"./Digraph":32,"./compoundify":45}],31:[function(require,module,exports){
|
||
var Graph = require("./Graph"),
|
||
compoundify = require("./compoundify");
|
||
|
||
var CGraph = compoundify(Graph);
|
||
|
||
module.exports = CGraph;
|
||
|
||
CGraph.fromGraph = function(src) {
|
||
var g = new CGraph(),
|
||
graphValue = src.graph();
|
||
|
||
if (graphValue !== undefined) {
|
||
g.graph(graphValue);
|
||
}
|
||
|
||
src.eachNode(function(u, value) {
|
||
if (value === undefined) {
|
||
g.addNode(u);
|
||
} else {
|
||
g.addNode(u, value);
|
||
}
|
||
});
|
||
src.eachEdge(function(e, u, v, value) {
|
||
if (value === undefined) {
|
||
g.addEdge(null, u, v);
|
||
} else {
|
||
g.addEdge(null, u, v, value);
|
||
}
|
||
});
|
||
return g;
|
||
};
|
||
|
||
CGraph.prototype.toString = function() {
|
||
return "CGraph " + JSON.stringify(this, null, 2);
|
||
};
|
||
|
||
},{"./Graph":33,"./compoundify":45}],32:[function(require,module,exports){
|
||
/*
|
||
* This file is organized with in the following order:
|
||
*
|
||
* Exports
|
||
* Graph constructors
|
||
* Graph queries (e.g. nodes(), edges()
|
||
* Graph mutators
|
||
* Helper functions
|
||
*/
|
||
|
||
var util = require("./util"),
|
||
BaseGraph = require("./BaseGraph"),
|
||
/* jshint -W079 */
|
||
Set = require("cp-data").Set;
|
||
/* jshint +W079 */
|
||
|
||
module.exports = Digraph;
|
||
|
||
/*
|
||
* Constructor to create a new directed multi-graph.
|
||
*/
|
||
function Digraph() {
|
||
BaseGraph.call(this);
|
||
|
||
/*! Map of sourceId -> {targetId -> Set of edge ids} */
|
||
this._inEdges = {};
|
||
|
||
/*! Map of targetId -> {sourceId -> Set of edge ids} */
|
||
this._outEdges = {};
|
||
}
|
||
|
||
Digraph.prototype = new BaseGraph();
|
||
Digraph.prototype.constructor = Digraph;
|
||
|
||
/*
|
||
* Always returns `true`.
|
||
*/
|
||
Digraph.prototype.isDirected = function() {
|
||
return true;
|
||
};
|
||
|
||
/*
|
||
* Returns all successors of the node with the id `u`. That is, all nodes
|
||
* that have the node `u` as their source are returned.
|
||
*
|
||
* If no node `u` exists in the graph this function throws an Error.
|
||
*
|
||
* @param {String} u a node id
|
||
*/
|
||
Digraph.prototype.successors = function(u) {
|
||
this._strictGetNode(u);
|
||
return Object.keys(this._outEdges[u])
|
||
.map(function(v) { return this._nodes[v].id; }, this);
|
||
};
|
||
|
||
/*
|
||
* Returns all predecessors of the node with the id `u`. That is, all nodes
|
||
* that have the node `u` as their target are returned.
|
||
*
|
||
* If no node `u` exists in the graph this function throws an Error.
|
||
*
|
||
* @param {String} u a node id
|
||
*/
|
||
Digraph.prototype.predecessors = function(u) {
|
||
this._strictGetNode(u);
|
||
return Object.keys(this._inEdges[u])
|
||
.map(function(v) { return this._nodes[v].id; }, this);
|
||
};
|
||
|
||
/*
|
||
* Returns all nodes that are adjacent to the node with the id `u`. In other
|
||
* words, this function returns the set of all successors and predecessors of
|
||
* node `u`.
|
||
*
|
||
* @param {String} u a node id
|
||
*/
|
||
Digraph.prototype.neighbors = function(u) {
|
||
return Set.union([this.successors(u), this.predecessors(u)]).keys();
|
||
};
|
||
|
||
/*
|
||
* Returns all nodes in the graph that have no in-edges.
|
||
*/
|
||
Digraph.prototype.sources = function() {
|
||
var self = this;
|
||
return this._filterNodes(function(u) {
|
||
// This could have better space characteristics if we had an inDegree function.
|
||
return self.inEdges(u).length === 0;
|
||
});
|
||
};
|
||
|
||
/*
|
||
* Returns all nodes in the graph that have no out-edges.
|
||
*/
|
||
Digraph.prototype.sinks = function() {
|
||
var self = this;
|
||
return this._filterNodes(function(u) {
|
||
// This could have better space characteristics if we have an outDegree function.
|
||
return self.outEdges(u).length === 0;
|
||
});
|
||
};
|
||
|
||
/*
|
||
* Returns the source node incident on the edge identified by the id `e`. If no
|
||
* such edge exists in the graph this function throws an Error.
|
||
*
|
||
* @param {String} e an edge id
|
||
*/
|
||
Digraph.prototype.source = function(e) {
|
||
return this._strictGetEdge(e).u;
|
||
};
|
||
|
||
/*
|
||
* Returns the target node incident on the edge identified by the id `e`. If no
|
||
* such edge exists in the graph this function throws an Error.
|
||
*
|
||
* @param {String} e an edge id
|
||
*/
|
||
Digraph.prototype.target = function(e) {
|
||
return this._strictGetEdge(e).v;
|
||
};
|
||
|
||
/*
|
||
* Returns an array of ids for all edges in the graph that have the node
|
||
* `target` as their target. If the node `target` is not in the graph this
|
||
* function raises an Error.
|
||
*
|
||
* Optionally a `source` node can also be specified. This causes the results
|
||
* to be filtered such that only edges from `source` to `target` are included.
|
||
* If the node `source` is specified but is not in the graph then this function
|
||
* raises an Error.
|
||
*
|
||
* @param {String} target the target node id
|
||
* @param {String} [source] an optional source node id
|
||
*/
|
||
Digraph.prototype.inEdges = function(target, source) {
|
||
this._strictGetNode(target);
|
||
var results = Set.union(util.values(this._inEdges[target])).keys();
|
||
if (arguments.length > 1) {
|
||
this._strictGetNode(source);
|
||
results = results.filter(function(e) { return this.source(e) === source; }, this);
|
||
}
|
||
return results;
|
||
};
|
||
|
||
/*
|
||
* Returns an array of ids for all edges in the graph that have the node
|
||
* `source` as their source. If the node `source` is not in the graph this
|
||
* function raises an Error.
|
||
*
|
||
* Optionally a `target` node may also be specified. This causes the results
|
||
* to be filtered such that only edges from `source` to `target` are included.
|
||
* If the node `target` is specified but is not in the graph then this function
|
||
* raises an Error.
|
||
*
|
||
* @param {String} source the source node id
|
||
* @param {String} [target] an optional target node id
|
||
*/
|
||
Digraph.prototype.outEdges = function(source, target) {
|
||
this._strictGetNode(source);
|
||
var results = Set.union(util.values(this._outEdges[source])).keys();
|
||
if (arguments.length > 1) {
|
||
this._strictGetNode(target);
|
||
results = results.filter(function(e) { return this.target(e) === target; }, this);
|
||
}
|
||
return results;
|
||
};
|
||
|
||
/*
|
||
* Returns an array of ids for all edges in the graph that have the `u` as
|
||
* their source or their target. If the node `u` is not in the graph this
|
||
* function raises an Error.
|
||
*
|
||
* Optionally a `v` node may also be specified. This causes the results to be
|
||
* filtered such that only edges between `u` and `v` - in either direction -
|
||
* are included. IF the node `v` is specified but not in the graph then this
|
||
* function raises an Error.
|
||
*
|
||
* @param {String} u the node for which to find incident edges
|
||
* @param {String} [v] option node that must be adjacent to `u`
|
||
*/
|
||
Digraph.prototype.incidentEdges = function(u, v) {
|
||
if (arguments.length > 1) {
|
||
return Set.union([this.outEdges(u, v), this.outEdges(v, u)]).keys();
|
||
} else {
|
||
return Set.union([this.inEdges(u), this.outEdges(u)]).keys();
|
||
}
|
||
};
|
||
|
||
/*
|
||
* Returns a string representation of this graph.
|
||
*/
|
||
Digraph.prototype.toString = function() {
|
||
return "Digraph " + JSON.stringify(this, null, 2);
|
||
};
|
||
|
||
/*
|
||
* Adds a new node with the id `u` to the graph and assigns it the value
|
||
* `value`. If a node with the id is already a part of the graph this function
|
||
* throws an Error.
|
||
*
|
||
* @param {String} u a node id
|
||
* @param {Object} [value] an optional value to attach to the node
|
||
*/
|
||
Digraph.prototype.addNode = function(u, value) {
|
||
u = BaseGraph.prototype.addNode.call(this, u, value);
|
||
this._inEdges[u] = {};
|
||
this._outEdges[u] = {};
|
||
return u;
|
||
};
|
||
|
||
/*
|
||
* Removes a node from the graph that has the id `u`. Any edges incident on the
|
||
* node are also removed. If the graph does not contain a node with the id this
|
||
* function will throw an Error.
|
||
*
|
||
* @param {String} u a node id
|
||
*/
|
||
Digraph.prototype.delNode = function(u) {
|
||
BaseGraph.prototype.delNode.call(this, u);
|
||
delete this._inEdges[u];
|
||
delete this._outEdges[u];
|
||
};
|
||
|
||
/*
|
||
* Adds a new edge to the graph with the id `e` from a node with the id `source`
|
||
* to a node with an id `target` and assigns it the value `value`. This graph
|
||
* allows more than one edge from `source` to `target` as long as the id `e`
|
||
* is unique in the set of edges. If `e` is `null` the graph will assign a
|
||
* unique identifier to the edge.
|
||
*
|
||
* If `source` or `target` are not present in the graph this function will
|
||
* throw an Error.
|
||
*
|
||
* @param {String} [e] an edge id
|
||
* @param {String} source the source node id
|
||
* @param {String} target the target node id
|
||
* @param {Object} [value] an optional value to attach to the edge
|
||
*/
|
||
Digraph.prototype.addEdge = function(e, source, target, value) {
|
||
return BaseGraph.prototype._addEdge.call(this, e, source, target, value,
|
||
this._inEdges, this._outEdges);
|
||
};
|
||
|
||
/*
|
||
* Removes an edge in the graph with the id `e`. If no edge in the graph has
|
||
* the id `e` this function will throw an Error.
|
||
*
|
||
* @param {String} e an edge id
|
||
*/
|
||
Digraph.prototype.delEdge = function(e) {
|
||
BaseGraph.prototype._delEdge.call(this, e, this._inEdges, this._outEdges);
|
||
};
|
||
|
||
// Unlike BaseGraph.filterNodes, this helper just returns nodes that
|
||
// satisfy a predicate.
|
||
Digraph.prototype._filterNodes = function(pred) {
|
||
var filtered = [];
|
||
this.eachNode(function(u) {
|
||
if (pred(u)) {
|
||
filtered.push(u);
|
||
}
|
||
});
|
||
return filtered;
|
||
};
|
||
|
||
|
||
},{"./BaseGraph":29,"./util":49,"cp-data":5}],33:[function(require,module,exports){
|
||
/*
|
||
* This file is organized with in the following order:
|
||
*
|
||
* Exports
|
||
* Graph constructors
|
||
* Graph queries (e.g. nodes(), edges()
|
||
* Graph mutators
|
||
* Helper functions
|
||
*/
|
||
|
||
var util = require("./util"),
|
||
BaseGraph = require("./BaseGraph"),
|
||
/* jshint -W079 */
|
||
Set = require("cp-data").Set;
|
||
/* jshint +W079 */
|
||
|
||
module.exports = Graph;
|
||
|
||
/*
|
||
* Constructor to create a new undirected multi-graph.
|
||
*/
|
||
function Graph() {
|
||
BaseGraph.call(this);
|
||
|
||
/*! Map of nodeId -> { otherNodeId -> Set of edge ids } */
|
||
this._incidentEdges = {};
|
||
}
|
||
|
||
Graph.prototype = new BaseGraph();
|
||
Graph.prototype.constructor = Graph;
|
||
|
||
/*
|
||
* Always returns `false`.
|
||
*/
|
||
Graph.prototype.isDirected = function() {
|
||
return false;
|
||
};
|
||
|
||
/*
|
||
* Returns all nodes that are adjacent to the node with the id `u`.
|
||
*
|
||
* @param {String} u a node id
|
||
*/
|
||
Graph.prototype.neighbors = function(u) {
|
||
this._strictGetNode(u);
|
||
return Object.keys(this._incidentEdges[u])
|
||
.map(function(v) { return this._nodes[v].id; }, this);
|
||
};
|
||
|
||
/*
|
||
* Returns an array of ids for all edges in the graph that are incident on `u`.
|
||
* If the node `u` is not in the graph this function raises an Error.
|
||
*
|
||
* Optionally a `v` node may also be specified. This causes the results to be
|
||
* filtered such that only edges between `u` and `v` are included. If the node
|
||
* `v` is specified but not in the graph then this function raises an Error.
|
||
*
|
||
* @param {String} u the node for which to find incident edges
|
||
* @param {String} [v] option node that must be adjacent to `u`
|
||
*/
|
||
Graph.prototype.incidentEdges = function(u, v) {
|
||
this._strictGetNode(u);
|
||
if (arguments.length > 1) {
|
||
this._strictGetNode(v);
|
||
return v in this._incidentEdges[u] ? this._incidentEdges[u][v].keys() : [];
|
||
} else {
|
||
return Set.union(util.values(this._incidentEdges[u])).keys();
|
||
}
|
||
};
|
||
|
||
/*
|
||
* Returns a string representation of this graph.
|
||
*/
|
||
Graph.prototype.toString = function() {
|
||
return "Graph " + JSON.stringify(this, null, 2);
|
||
};
|
||
|
||
/*
|
||
* Adds a new node with the id `u` to the graph and assigns it the value
|
||
* `value`. If a node with the id is already a part of the graph this function
|
||
* throws an Error.
|
||
*
|
||
* @param {String} u a node id
|
||
* @param {Object} [value] an optional value to attach to the node
|
||
*/
|
||
Graph.prototype.addNode = function(u, value) {
|
||
u = BaseGraph.prototype.addNode.call(this, u, value);
|
||
this._incidentEdges[u] = {};
|
||
return u;
|
||
};
|
||
|
||
/*
|
||
* Removes a node from the graph that has the id `u`. Any edges incident on the
|
||
* node are also removed. If the graph does not contain a node with the id this
|
||
* function will throw an Error.
|
||
*
|
||
* @param {String} u a node id
|
||
*/
|
||
Graph.prototype.delNode = function(u) {
|
||
BaseGraph.prototype.delNode.call(this, u);
|
||
delete this._incidentEdges[u];
|
||
};
|
||
|
||
/*
|
||
* Adds a new edge to the graph with the id `e` between a node with the id `u`
|
||
* and a node with an id `v` and assigns it the value `value`. This graph
|
||
* allows more than one edge between `u` and `v` as long as the id `e`
|
||
* is unique in the set of edges. If `e` is `null` the graph will assign a
|
||
* unique identifier to the edge.
|
||
*
|
||
* If `u` or `v` are not present in the graph this function will throw an
|
||
* Error.
|
||
*
|
||
* @param {String} [e] an edge id
|
||
* @param {String} u the node id of one of the adjacent nodes
|
||
* @param {String} v the node id of the other adjacent node
|
||
* @param {Object} [value] an optional value to attach to the edge
|
||
*/
|
||
Graph.prototype.addEdge = function(e, u, v, value) {
|
||
return BaseGraph.prototype._addEdge.call(this, e, u, v, value,
|
||
this._incidentEdges, this._incidentEdges);
|
||
};
|
||
|
||
/*
|
||
* Removes an edge in the graph with the id `e`. If no edge in the graph has
|
||
* the id `e` this function will throw an Error.
|
||
*
|
||
* @param {String} e an edge id
|
||
*/
|
||
Graph.prototype.delEdge = function(e) {
|
||
BaseGraph.prototype._delEdge.call(this, e, this._incidentEdges, this._incidentEdges);
|
||
};
|
||
|
||
|
||
},{"./BaseGraph":29,"./util":49,"cp-data":5}],34:[function(require,module,exports){
|
||
/* jshint -W079 */
|
||
var Set = require("cp-data").Set;
|
||
/* jshint +W079 */
|
||
|
||
module.exports = components;
|
||
|
||
/**
|
||
* Finds all [connected components][] in a graph and returns an array of these
|
||
* components. Each component is itself an array that contains the ids of nodes
|
||
* in the component.
|
||
*
|
||
* This function only works with undirected Graphs.
|
||
*
|
||
* [connected components]: http://en.wikipedia.org/wiki/Connected_component_(graph_theory)
|
||
*
|
||
* @param {Graph} g the graph to search for components
|
||
*/
|
||
function components(g) {
|
||
var results = [];
|
||
var visited = new Set();
|
||
|
||
function dfs(v, component) {
|
||
if (!visited.has(v)) {
|
||
visited.add(v);
|
||
component.push(v);
|
||
g.neighbors(v).forEach(function(w) {
|
||
dfs(w, component);
|
||
});
|
||
}
|
||
}
|
||
|
||
g.nodes().forEach(function(v) {
|
||
var component = [];
|
||
dfs(v, component);
|
||
if (component.length > 0) {
|
||
results.push(component);
|
||
}
|
||
});
|
||
|
||
return results;
|
||
}
|
||
|
||
},{"cp-data":5}],35:[function(require,module,exports){
|
||
var PriorityQueue = require("cp-data").PriorityQueue;
|
||
|
||
module.exports = dijkstra;
|
||
|
||
/**
|
||
* This function is an implementation of [Dijkstra's algorithm][] which finds
|
||
* the shortest path from **source** to all other nodes in **g**. This
|
||
* function returns a map of `u -> { distance, predecessor }`. The distance
|
||
* property holds the sum of the weights from **source** to `u` along the
|
||
* shortest path or `Number.POSITIVE_INFINITY` if there is no path from
|
||
* **source**. The predecessor property can be used to walk the individual
|
||
* elements of the path from **source** to **u** in reverse order.
|
||
*
|
||
* This function takes an optional `weightFunc(e)` which returns the
|
||
* weight of the edge `e`. If no weightFunc is supplied then each edge is
|
||
* assumed to have a weight of 1. This function throws an Error if any of
|
||
* the traversed edges have a negative edge weight.
|
||
*
|
||
* This function takes an optional `incidentFunc(u)` which returns the ids of
|
||
* all edges incident to the node `u` for the purposes of shortest path
|
||
* traversal. By default this function uses the `g.outEdges` for Digraphs and
|
||
* `g.incidentEdges` for Graphs.
|
||
*
|
||
* This function takes `O((|E| + |V|) * log |V|)` time.
|
||
*
|
||
* [Dijkstra's algorithm]: http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
|
||
*
|
||
* @param {Graph} g the graph to search for shortest paths from **source**
|
||
* @param {Object} source the source from which to start the search
|
||
* @param {Function} [weightFunc] optional weight function
|
||
* @param {Function} [incidentFunc] optional incident function
|
||
*/
|
||
function dijkstra(g, source, weightFunc, incidentFunc) {
|
||
var results = {},
|
||
pq = new PriorityQueue();
|
||
|
||
function updateNeighbors(e) {
|
||
var incidentNodes = g.incidentNodes(e),
|
||
v = incidentNodes[0] !== u ? incidentNodes[0] : incidentNodes[1],
|
||
vEntry = results[v],
|
||
weight = weightFunc(e),
|
||
distance = uEntry.distance + weight;
|
||
|
||
if (weight < 0) {
|
||
throw new Error("dijkstra does not allow negative edge weights. Bad edge: " + e + " Weight: " + weight);
|
||
}
|
||
|
||
if (distance < vEntry.distance) {
|
||
vEntry.distance = distance;
|
||
vEntry.predecessor = u;
|
||
pq.decrease(v, distance);
|
||
}
|
||
}
|
||
|
||
weightFunc = weightFunc || function() { return 1; };
|
||
incidentFunc = incidentFunc || (g.isDirected()
|
||
? function(u) { return g.outEdges(u); }
|
||
: function(u) { return g.incidentEdges(u); });
|
||
|
||
g.eachNode(function(u) {
|
||
var distance = u === source ? 0 : Number.POSITIVE_INFINITY;
|
||
results[u] = { distance: distance };
|
||
pq.add(u, distance);
|
||
});
|
||
|
||
var u, uEntry;
|
||
while (pq.size() > 0) {
|
||
u = pq.removeMin();
|
||
uEntry = results[u];
|
||
if (uEntry.distance === Number.POSITIVE_INFINITY) {
|
||
break;
|
||
}
|
||
|
||
incidentFunc(u).forEach(updateNeighbors);
|
||
}
|
||
|
||
return results;
|
||
}
|
||
|
||
},{"cp-data":5}],36:[function(require,module,exports){
|
||
var dijkstra = require("./dijkstra");
|
||
|
||
module.exports = dijkstraAll;
|
||
|
||
/**
|
||
* This function finds the shortest path from each node to every other
|
||
* reachable node in the graph. It is similar to [alg.dijkstra][], but
|
||
* instead of returning a single-source array, it returns a mapping of
|
||
* of `source -> alg.dijksta(g, source, weightFunc, incidentFunc)`.
|
||
*
|
||
* This function takes an optional `weightFunc(e)` which returns the
|
||
* weight of the edge `e`. If no weightFunc is supplied then each edge is
|
||
* assumed to have a weight of 1. This function throws an Error if any of
|
||
* the traversed edges have a negative edge weight.
|
||
*
|
||
* This function takes an optional `incidentFunc(u)` which returns the ids of
|
||
* all edges incident to the node `u` for the purposes of shortest path
|
||
* traversal. By default this function uses the `outEdges` function on the
|
||
* supplied graph.
|
||
*
|
||
* This function takes `O(|V| * (|E| + |V|) * log |V|)` time.
|
||
*
|
||
* [alg.dijkstra]: dijkstra.js.html#dijkstra
|
||
*
|
||
* @param {Graph} g the graph to search for shortest paths from **source**
|
||
* @param {Function} [weightFunc] optional weight function
|
||
* @param {Function} [incidentFunc] optional incident function
|
||
*/
|
||
function dijkstraAll(g, weightFunc, incidentFunc) {
|
||
var results = {};
|
||
g.eachNode(function(u) {
|
||
results[u] = dijkstra(g, u, weightFunc, incidentFunc);
|
||
});
|
||
return results;
|
||
}
|
||
|
||
},{"./dijkstra":35}],37:[function(require,module,exports){
|
||
var tarjan = require("./tarjan");
|
||
|
||
module.exports = findCycles;
|
||
|
||
/*
|
||
* Given a Digraph **g** this function returns all nodes that are part of a
|
||
* cycle. Since there may be more than one cycle in a graph this function
|
||
* returns an array of these cycles, where each cycle is itself represented
|
||
* by an array of ids for each node involved in that cycle.
|
||
*
|
||
* [alg.isAcyclic][] is more efficient if you only need to determine whether
|
||
* a graph has a cycle or not.
|
||
*
|
||
* [alg.isAcyclic]: isAcyclic.js.html#isAcyclic
|
||
*
|
||
* @param {Digraph} g the graph to search for cycles.
|
||
*/
|
||
function findCycles(g) {
|
||
return tarjan(g).filter(function(cmpt) { return cmpt.length > 1; });
|
||
}
|
||
|
||
},{"./tarjan":43}],38:[function(require,module,exports){
|
||
module.exports = floydWarshall;
|
||
|
||
/**
|
||
* This function is an implementation of the [Floyd-Warshall algorithm][],
|
||
* which finds the shortest path from each node to every other reachable node
|
||
* in the graph. It is similar to [alg.dijkstraAll][], but it handles negative
|
||
* edge weights and is more efficient for some types of graphs. This function
|
||
* returns a map of `source -> { target -> { distance, predecessor }`. The
|
||
* distance property holds the sum of the weights from `source` to `target`
|
||
* along the shortest path of `Number.POSITIVE_INFINITY` if there is no path
|
||
* from `source`. The predecessor property can be used to walk the individual
|
||
* elements of the path from `source` to `target` in reverse order.
|
||
*
|
||
* This function takes an optional `weightFunc(e)` which returns the
|
||
* weight of the edge `e`. If no weightFunc is supplied then each edge is
|
||
* assumed to have a weight of 1.
|
||
*
|
||
* This function takes an optional `incidentFunc(u)` which returns the ids of
|
||
* all edges incident to the node `u` for the purposes of shortest path
|
||
* traversal. By default this function uses the `outEdges` function on the
|
||
* supplied graph.
|
||
*
|
||
* This algorithm takes O(|V|^3) time.
|
||
*
|
||
* [Floyd-Warshall algorithm]: https://en.wikipedia.org/wiki/Floyd-Warshall_algorithm
|
||
* [alg.dijkstraAll]: dijkstraAll.js.html#dijkstraAll
|
||
*
|
||
* @param {Graph} g the graph to search for shortest paths from **source**
|
||
* @param {Function} [weightFunc] optional weight function
|
||
* @param {Function} [incidentFunc] optional incident function
|
||
*/
|
||
function floydWarshall(g, weightFunc, incidentFunc) {
|
||
var results = {},
|
||
nodes = g.nodes();
|
||
|
||
weightFunc = weightFunc || function() { return 1; };
|
||
incidentFunc = incidentFunc || (g.isDirected()
|
||
? function(u) { return g.outEdges(u); }
|
||
: function(u) { return g.incidentEdges(u); });
|
||
|
||
nodes.forEach(function(u) {
|
||
results[u] = {};
|
||
results[u][u] = { distance: 0 };
|
||
nodes.forEach(function(v) {
|
||
if (u !== v) {
|
||
results[u][v] = { distance: Number.POSITIVE_INFINITY };
|
||
}
|
||
});
|
||
incidentFunc(u).forEach(function(e) {
|
||
var incidentNodes = g.incidentNodes(e),
|
||
v = incidentNodes[0] !== u ? incidentNodes[0] : incidentNodes[1],
|
||
d = weightFunc(e);
|
||
if (d < results[u][v].distance) {
|
||
results[u][v] = { distance: d, predecessor: u };
|
||
}
|
||
});
|
||
});
|
||
|
||
nodes.forEach(function(k) {
|
||
var rowK = results[k];
|
||
nodes.forEach(function(i) {
|
||
var rowI = results[i];
|
||
nodes.forEach(function(j) {
|
||
var ik = rowI[k];
|
||
var kj = rowK[j];
|
||
var ij = rowI[j];
|
||
var altDistance = ik.distance + kj.distance;
|
||
if (altDistance < ij.distance) {
|
||
ij.distance = altDistance;
|
||
ij.predecessor = kj.predecessor;
|
||
}
|
||
});
|
||
});
|
||
});
|
||
|
||
return results;
|
||
}
|
||
|
||
},{}],39:[function(require,module,exports){
|
||
var topsort = require("./topsort");
|
||
|
||
module.exports = isAcyclic;
|
||
|
||
/*
|
||
* Given a Digraph **g** this function returns `true` if the graph has no
|
||
* cycles and returns `false` if it does. This algorithm returns as soon as it
|
||
* detects the first cycle.
|
||
*
|
||
* Use [alg.findCycles][] if you need the actual list of cycles in a graph.
|
||
*
|
||
* [alg.findCycles]: findCycles.js.html#findCycles
|
||
*
|
||
* @param {Digraph} g the graph to test for cycles
|
||
*/
|
||
function isAcyclic(g) {
|
||
try {
|
||
topsort(g);
|
||
} catch (e) {
|
||
if (e instanceof topsort.CycleException) return false;
|
||
throw e;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
},{"./topsort":44}],40:[function(require,module,exports){
|
||
/* jshint -W079 */
|
||
var Set = require("cp-data").Set;
|
||
/* jshint +W079 */
|
||
|
||
module.exports = postorder;
|
||
|
||
// Postorder traversal of g, calling f for each visited node. Assumes the graph
|
||
// is a tree.
|
||
function postorder(g, root, f) {
|
||
var visited = new Set();
|
||
if (g.isDirected()) {
|
||
throw new Error("This function only works for undirected graphs");
|
||
}
|
||
function dfs(u, prev) {
|
||
if (visited.has(u)) {
|
||
throw new Error("The input graph is not a tree: " + g);
|
||
}
|
||
visited.add(u);
|
||
g.neighbors(u).forEach(function(v) {
|
||
if (v !== prev) dfs(v, u);
|
||
});
|
||
f(u);
|
||
}
|
||
dfs(root);
|
||
}
|
||
|
||
},{"cp-data":5}],41:[function(require,module,exports){
|
||
/* jshint -W079 */
|
||
var Set = require("cp-data").Set;
|
||
/* jshint +W079 */
|
||
|
||
module.exports = preorder;
|
||
|
||
// Preorder traversal of g, calling f for each visited node. Assumes the graph
|
||
// is a tree.
|
||
function preorder(g, root, f) {
|
||
var visited = new Set();
|
||
if (g.isDirected()) {
|
||
throw new Error("This function only works for undirected graphs");
|
||
}
|
||
function dfs(u, prev) {
|
||
if (visited.has(u)) {
|
||
throw new Error("The input graph is not a tree: " + g);
|
||
}
|
||
visited.add(u);
|
||
f(u);
|
||
g.neighbors(u).forEach(function(v) {
|
||
if (v !== prev) dfs(v, u);
|
||
});
|
||
}
|
||
dfs(root);
|
||
}
|
||
|
||
},{"cp-data":5}],42:[function(require,module,exports){
|
||
var Graph = require("../Graph"),
|
||
PriorityQueue = require("cp-data").PriorityQueue;
|
||
|
||
module.exports = prim;
|
||
|
||
/**
|
||
* [Prim's algorithm][] takes a connected undirected graph and generates a
|
||
* [minimum spanning tree][]. This function returns the minimum spanning
|
||
* tree as an undirected graph. This algorithm is derived from the description
|
||
* in "Introduction to Algorithms", Third Edition, Cormen, et al., Pg 634.
|
||
*
|
||
* This function takes a `weightFunc(e)` which returns the weight of the edge
|
||
* `e`. It throws an Error if the graph is not connected.
|
||
*
|
||
* This function takes `O(|E| log |V|)` time.
|
||
*
|
||
* [Prim's algorithm]: https://en.wikipedia.org/wiki/Prim's_algorithm
|
||
* [minimum spanning tree]: https://en.wikipedia.org/wiki/Minimum_spanning_tree
|
||
*
|
||
* @param {Graph} g the graph used to generate the minimum spanning tree
|
||
* @param {Function} weightFunc the weight function to use
|
||
*/
|
||
function prim(g, weightFunc) {
|
||
var result = new Graph(),
|
||
parents = {},
|
||
pq = new PriorityQueue(),
|
||
u;
|
||
|
||
function updateNeighbors(e) {
|
||
var incidentNodes = g.incidentNodes(e),
|
||
v = incidentNodes[0] !== u ? incidentNodes[0] : incidentNodes[1],
|
||
pri = pq.priority(v);
|
||
if (pri !== undefined) {
|
||
var edgeWeight = weightFunc(e);
|
||
if (edgeWeight < pri) {
|
||
parents[v] = u;
|
||
pq.decrease(v, edgeWeight);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (g.order() === 0) {
|
||
return result;
|
||
}
|
||
|
||
g.eachNode(function(u) {
|
||
pq.add(u, Number.POSITIVE_INFINITY);
|
||
result.addNode(u);
|
||
});
|
||
|
||
// Start from an arbitrary node
|
||
pq.decrease(g.nodes()[0], 0);
|
||
|
||
var init = false;
|
||
while (pq.size() > 0) {
|
||
u = pq.removeMin();
|
||
if (u in parents) {
|
||
result.addEdge(null, u, parents[u]);
|
||
} else if (init) {
|
||
throw new Error("Input graph is not connected: " + g);
|
||
} else {
|
||
init = true;
|
||
}
|
||
|
||
g.incidentEdges(u).forEach(updateNeighbors);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
},{"../Graph":33,"cp-data":5}],43:[function(require,module,exports){
|
||
module.exports = tarjan;
|
||
|
||
/**
|
||
* This function is an implementation of [Tarjan's algorithm][] which finds
|
||
* all [strongly connected components][] in the directed graph **g**. Each
|
||
* strongly connected component is composed of nodes that can reach all other
|
||
* nodes in the component via directed edges. A strongly connected component
|
||
* can consist of a single node if that node cannot both reach and be reached
|
||
* by any other specific node in the graph. Components of more than one node
|
||
* are guaranteed to have at least one cycle.
|
||
*
|
||
* This function returns an array of components. Each component is itself an
|
||
* array that contains the ids of all nodes in the component.
|
||
*
|
||
* [Tarjan's algorithm]: http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm
|
||
* [strongly connected components]: http://en.wikipedia.org/wiki/Strongly_connected_component
|
||
*
|
||
* @param {Digraph} g the graph to search for strongly connected components
|
||
*/
|
||
function tarjan(g) {
|
||
if (!g.isDirected()) {
|
||
throw new Error("tarjan can only be applied to a directed graph. Bad input: " + g);
|
||
}
|
||
|
||
var index = 0,
|
||
stack = [],
|
||
visited = {}, // node id -> { onStack, lowlink, index }
|
||
results = [];
|
||
|
||
function dfs(u) {
|
||
var entry = visited[u] = {
|
||
onStack: true,
|
||
lowlink: index,
|
||
index: index++
|
||
};
|
||
stack.push(u);
|
||
|
||
g.successors(u).forEach(function(v) {
|
||
if (!(v in visited)) {
|
||
dfs(v);
|
||
entry.lowlink = Math.min(entry.lowlink, visited[v].lowlink);
|
||
} else if (visited[v].onStack) {
|
||
entry.lowlink = Math.min(entry.lowlink, visited[v].index);
|
||
}
|
||
});
|
||
|
||
if (entry.lowlink === entry.index) {
|
||
var cmpt = [],
|
||
v;
|
||
do {
|
||
v = stack.pop();
|
||
visited[v].onStack = false;
|
||
cmpt.push(v);
|
||
} while (u !== v);
|
||
results.push(cmpt);
|
||
}
|
||
}
|
||
|
||
g.nodes().forEach(function(u) {
|
||
if (!(u in visited)) {
|
||
dfs(u);
|
||
}
|
||
});
|
||
|
||
return results;
|
||
}
|
||
|
||
},{}],44:[function(require,module,exports){
|
||
module.exports = topsort;
|
||
topsort.CycleException = CycleException;
|
||
|
||
/*
|
||
* Given a graph **g**, this function returns an ordered list of nodes such
|
||
* that for each edge `u -> v`, `u` appears before `v` in the list. If the
|
||
* graph has a cycle it is impossible to generate such a list and
|
||
* **CycleException** is thrown.
|
||
*
|
||
* See [topological sorting](https://en.wikipedia.org/wiki/Topological_sorting)
|
||
* for more details about how this algorithm works.
|
||
*
|
||
* @param {Digraph} g the graph to sort
|
||
*/
|
||
function topsort(g) {
|
||
if (!g.isDirected()) {
|
||
throw new Error("topsort can only be applied to a directed graph. Bad input: " + g);
|
||
}
|
||
|
||
var visited = {};
|
||
var stack = {};
|
||
var results = [];
|
||
|
||
function visit(node) {
|
||
if (node in stack) {
|
||
throw new CycleException();
|
||
}
|
||
|
||
if (!(node in visited)) {
|
||
stack[node] = true;
|
||
visited[node] = true;
|
||
g.predecessors(node).forEach(function(pred) {
|
||
visit(pred);
|
||
});
|
||
delete stack[node];
|
||
results.push(node);
|
||
}
|
||
}
|
||
|
||
var sinks = g.sinks();
|
||
if (g.order() !== 0 && sinks.length === 0) {
|
||
throw new CycleException();
|
||
}
|
||
|
||
g.sinks().forEach(function(sink) {
|
||
visit(sink);
|
||
});
|
||
|
||
return results;
|
||
}
|
||
|
||
function CycleException() {}
|
||
|
||
CycleException.prototype.toString = function() {
|
||
return "Graph has at least one cycle";
|
||
};
|
||
|
||
},{}],45:[function(require,module,exports){
|
||
// This file provides a helper function that mixes-in Dot behavior to an
|
||
// existing graph prototype.
|
||
|
||
/* jshint -W079 */
|
||
var Set = require("cp-data").Set;
|
||
/* jshint +W079 */
|
||
|
||
module.exports = compoundify;
|
||
|
||
// Extends the given SuperConstructor with the ability for nodes to contain
|
||
// other nodes. A special node id `null` is used to indicate the root graph.
|
||
function compoundify(SuperConstructor) {
|
||
function Constructor() {
|
||
SuperConstructor.call(this);
|
||
|
||
// Map of object id -> parent id (or null for root graph)
|
||
this._parents = {};
|
||
|
||
// Map of id (or null) -> children set
|
||
this._children = {};
|
||
this._children[null] = new Set();
|
||
}
|
||
|
||
Constructor.prototype = new SuperConstructor();
|
||
Constructor.prototype.constructor = Constructor;
|
||
|
||
Constructor.prototype.parent = function(u, parent) {
|
||
this._strictGetNode(u);
|
||
|
||
if (arguments.length < 2) {
|
||
return this._parents[u];
|
||
}
|
||
|
||
if (u === parent) {
|
||
throw new Error("Cannot make " + u + " a parent of itself");
|
||
}
|
||
if (parent !== null) {
|
||
this._strictGetNode(parent);
|
||
}
|
||
|
||
this._children[this._parents[u]].remove(u);
|
||
this._parents[u] = parent;
|
||
this._children[parent].add(u);
|
||
};
|
||
|
||
Constructor.prototype.children = function(u) {
|
||
if (u !== null) {
|
||
this._strictGetNode(u);
|
||
}
|
||
return this._children[u].keys();
|
||
};
|
||
|
||
Constructor.prototype.addNode = function(u, value) {
|
||
u = SuperConstructor.prototype.addNode.call(this, u, value);
|
||
this._parents[u] = null;
|
||
this._children[u] = new Set();
|
||
this._children[null].add(u);
|
||
return u;
|
||
};
|
||
|
||
Constructor.prototype.delNode = function(u) {
|
||
// Promote all children to the parent of the subgraph
|
||
var parent = this.parent(u);
|
||
this._children[u].keys().forEach(function(child) {
|
||
this.parent(child, parent);
|
||
}, this);
|
||
|
||
this._children[parent].remove(u);
|
||
delete this._parents[u];
|
||
delete this._children[u];
|
||
|
||
return SuperConstructor.prototype.delNode.call(this, u);
|
||
};
|
||
|
||
Constructor.prototype.copy = function() {
|
||
var copy = SuperConstructor.prototype.copy.call(this);
|
||
this.nodes().forEach(function(u) {
|
||
copy.parent(u, this.parent(u));
|
||
}, this);
|
||
return copy;
|
||
};
|
||
|
||
Constructor.prototype.filterNodes = function(filter) {
|
||
var self = this,
|
||
copy = SuperConstructor.prototype.filterNodes.call(this, filter);
|
||
|
||
var parents = {};
|
||
function findParent(u) {
|
||
var parent = self.parent(u);
|
||
if (parent === null || copy.hasNode(parent)) {
|
||
parents[u] = parent;
|
||
return parent;
|
||
} else if (parent in parents) {
|
||
return parents[parent];
|
||
} else {
|
||
return findParent(parent);
|
||
}
|
||
}
|
||
|
||
copy.eachNode(function(u) { copy.parent(u, findParent(u)); });
|
||
|
||
return copy;
|
||
};
|
||
|
||
return Constructor;
|
||
}
|
||
|
||
},{"cp-data":5}],46:[function(require,module,exports){
|
||
var Graph = require("../Graph"),
|
||
Digraph = require("../Digraph"),
|
||
CGraph = require("../CGraph"),
|
||
CDigraph = require("../CDigraph");
|
||
|
||
exports.decode = function(nodes, edges, Ctor) {
|
||
Ctor = Ctor || Digraph;
|
||
|
||
if (typeOf(nodes) !== "Array") {
|
||
throw new Error("nodes is not an Array");
|
||
}
|
||
|
||
if (typeOf(edges) !== "Array") {
|
||
throw new Error("edges is not an Array");
|
||
}
|
||
|
||
if (typeof Ctor === "string") {
|
||
switch(Ctor) {
|
||
case "graph": Ctor = Graph; break;
|
||
case "digraph": Ctor = Digraph; break;
|
||
case "cgraph": Ctor = CGraph; break;
|
||
case "cdigraph": Ctor = CDigraph; break;
|
||
default: throw new Error("Unrecognized graph type: " + Ctor);
|
||
}
|
||
}
|
||
|
||
var graph = new Ctor();
|
||
|
||
nodes.forEach(function(u) {
|
||
graph.addNode(u.id, u.value);
|
||
});
|
||
|
||
// If the graph is compound, set up children...
|
||
if (graph.parent) {
|
||
nodes.forEach(function(u) {
|
||
if (u.children) {
|
||
u.children.forEach(function(v) {
|
||
graph.parent(v, u.id);
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
edges.forEach(function(e) {
|
||
graph.addEdge(e.id, e.u, e.v, e.value);
|
||
});
|
||
|
||
return graph;
|
||
};
|
||
|
||
exports.encode = function(graph) {
|
||
var nodes = [];
|
||
var edges = [];
|
||
|
||
graph.eachNode(function(u, value) {
|
||
var node = {id: u, value: value};
|
||
if (graph.children) {
|
||
var children = graph.children(u);
|
||
if (children.length) {
|
||
node.children = children;
|
||
}
|
||
}
|
||
nodes.push(node);
|
||
});
|
||
|
||
graph.eachEdge(function(e, u, v, value) {
|
||
edges.push({id: e, u: u, v: v, value: value});
|
||
});
|
||
|
||
var type;
|
||
if (graph instanceof CDigraph) {
|
||
type = "cdigraph";
|
||
} else if (graph instanceof CGraph) {
|
||
type = "cgraph";
|
||
} else if (graph instanceof Digraph) {
|
||
type = "digraph";
|
||
} else if (graph instanceof Graph) {
|
||
type = "graph";
|
||
} else {
|
||
throw new Error("Couldn't determine type of graph: " + graph);
|
||
}
|
||
|
||
return { nodes: nodes, edges: edges, type: type };
|
||
};
|
||
|
||
function typeOf(obj) {
|
||
return Object.prototype.toString.call(obj).slice(8, -1);
|
||
}
|
||
|
||
},{"../CDigraph":30,"../CGraph":31,"../Digraph":32,"../Graph":33}],47:[function(require,module,exports){
|
||
/* jshint -W079 */
|
||
var Set = require("cp-data").Set;
|
||
/* jshint +W079 */
|
||
|
||
exports.all = function() {
|
||
return function() { return true; };
|
||
};
|
||
|
||
exports.nodesFromList = function(nodes) {
|
||
var set = new Set(nodes);
|
||
return function(u) {
|
||
return set.has(u);
|
||
};
|
||
};
|
||
|
||
},{"cp-data":5}],48:[function(require,module,exports){
|
||
var Graph = require("./Graph"),
|
||
Digraph = require("./Digraph");
|
||
|
||
// Side-effect based changes are lousy, but node doesn't seem to resolve the
|
||
// requires cycle.
|
||
|
||
/**
|
||
* Returns a new directed graph using the nodes and edges from this graph. The
|
||
* new graph will have the same nodes, but will have twice the number of edges:
|
||
* each edge is split into two edges with opposite directions. Edge ids,
|
||
* consequently, are not preserved by this transformation.
|
||
*/
|
||
Graph.prototype.toDigraph =
|
||
Graph.prototype.asDirected = function() {
|
||
var g = new Digraph();
|
||
this.eachNode(function(u, value) { g.addNode(u, value); });
|
||
this.eachEdge(function(e, u, v, value) {
|
||
g.addEdge(null, u, v, value);
|
||
g.addEdge(null, v, u, value);
|
||
});
|
||
return g;
|
||
};
|
||
|
||
/**
|
||
* Returns a new undirected graph using the nodes and edges from this graph.
|
||
* The new graph will have the same nodes, but the edges will be made
|
||
* undirected. Edge ids are preserved in this transformation.
|
||
*/
|
||
Digraph.prototype.toGraph =
|
||
Digraph.prototype.asUndirected = function() {
|
||
var g = new Graph();
|
||
this.eachNode(function(u, value) { g.addNode(u, value); });
|
||
this.eachEdge(function(e, u, v, value) {
|
||
g.addEdge(e, u, v, value);
|
||
});
|
||
return g;
|
||
};
|
||
|
||
},{"./Digraph":32,"./Graph":33}],49:[function(require,module,exports){
|
||
// Returns an array of all values for properties of **o**.
|
||
exports.values = function(o) {
|
||
var ks = Object.keys(o),
|
||
len = ks.length,
|
||
result = new Array(len),
|
||
i;
|
||
for (i = 0; i < len; ++i) {
|
||
result[i] = o[ks[i]];
|
||
}
|
||
return result;
|
||
};
|
||
|
||
},{}],50:[function(require,module,exports){
|
||
module.exports = '0.7.4';
|
||
|
||
},{}]},{},[1])
|
||
; |