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 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'));
// 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); });
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);
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();
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);
}
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]);
}
}
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);
}
/** *Insertsanewkeyintothepriorityqueue.Ifthekeyalreadyexistsin *thequeuethisfunctionreturns`false`;otherwiseitwillreturn`true`. *Takes`O(n)`time. * *@param{Object}keythekeytoadd *@param{Number}prioritytheinitialpriorityforthekey
*/
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); returntrue;
} returnfalse;
};
/** *Removesandreturnsthesmallestkeyinthequeue.Takes`O(logn)`time.
*/
PriorityQueue.prototype.removeMin = function() { this._swap(0, this._arr.length - 1); var min = this._arr.pop(); deletethis._keyIndices[min.key]; this._heapify(0); return min.key;
};
/** *Decreasesthepriorityfor**key**to**priority**.Ifthenewpriorityis *greaterthanthepreviouspriority,thisfunctionwillthrowanError. * *@param{Object}keythekeyforwhichtoraisepriority *@param{Number}prioritythenewpriorityforthekey
*/
PriorityQueue.prototype.decrease = function(key, priority) { var index = this._keyIndices[key]; if (priority > this._arr[index].priority) { thrownew 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');
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;
};
/** *ReturnsanewSetthatrepresentsthesetunionofthearrayofgivensets.
*/
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];
}
}
/** *RemovesakeyfromthisSet.Ifthekeywasremovedthisfunctionreturns *`true`.Ifnot,itreturns`false`.Takes`O(1)`time.
*/
Set.prototype.remove = function(key) { if (key in this._keys) { deletethis._keys[key];
--this._size; returntrue;
} returnfalse;
};
/* *Returnsanarrayofallvaluesforpropertiesof**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;
}
/* *Slightlyadaptedpolyfillfrom *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. thrownew TypeError( 'Array.prototype.reduce called on null or undefined');
} if ('function' !== typeof callback) { thrownew 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) { thrownew 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);
};
}
},{"./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')();
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);
}
}
/* *Thisfunctionisresponsiblefor'normalizing'thegraph.Theprocessof *normalizationensuresthatnoedgeinthegraphhasspansmorethanone *rank.Todothisitinsertsdummynodesasneededandlinksthembyadding *dummyedges.Thisfunctionkeepsenoughinformationinthedummynodesand *edgestoensurethattheoriginalgraphcanbereconstructedlater. * *Thismethodassumesthattheinputgraphiscyclefree.
*/ 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; elseif (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);
}
});
}
/* *Reconstructsthegraphasitwasbeforenormalization.Thepositionsof *dummynodesareusedtobuildanarrayofpointsfortheoriginal'long' *edge.Dummynodesandedgesareremoved.
*/ 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);
}
});
}
/* *Foreachedgethatwasreversedduringthe`acyclic`step,reverseits *arrayofpoints.
*/ 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);
});
// The maximum number of sweeps to perform before finishing the order phase. var DEFAULT_MAX_SWEEPS = 24;
order.DEFAULT_MAX_SWEEPS = DEFAULT_MAX_SWEEPS;
/* *Runstheorderphasewiththespecified`graph,`maxSweeps`,and *`debugLevel`.If`maxSweeps`isnotspecifiedweuse`DEFAULT_MAX_SWEEPS`. *If`debugLevel`isnotsetweassume0.
*/ 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; });
});
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;
/* *Returnsthecrosscountforthegivengraph.
*/ 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;
}
/* *Thisfunctionsearchesthrougharankedandorderedgraphandcountsthe *numberofedgesthatcross.Thisalgorithmisderivedfrom: * *W.Barthetal.,BilayerCrossCounting,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;
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. /* functionsortLayerSubgraph(g,sg,cg,weights){ cg=cg?cg.filterNodes(nodesFromList(g.children(sg))):newDigraph();
functionresolveViolatedConstraints(g,cg,nodeData){ // Removes nodes `u` and `v` from `cg` and makes any edges incident on them // incident on `w` instead. functioncollapseNodes(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); });
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);
});
}
/* *GenerateanIDthatcanbeusedtorepresentanyundirectededgethatis *incidenton`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) { 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
// 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
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;
}
});
});
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 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;
});
});
}
};
// 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);
}
/* *Expandselfloopsintothreedummynodes.Onewillsitabovetheincident *node,onewillbeatthesamelevel,andonebelow.Theresultlookslike: * */--<--x--->--\ *nodey *\--<--z--->--/ * *Dummynodesx,y,zgiveustheshapeofaloopandnodeyiswhereweplace *thelabel. * *TODO:consolidateknowledgeofdummynodeconstruction. *TODO:supportminLen=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');
/* *Thisfunctiontakesadirectedgraphthatmayhavecyclesandreversesedges *asappropriatetobreakthesecycles.Eachreversededgeisassigneda *`reversed`attributewiththevalue`true`. * *Thereshouldbenoselfloopsinthegraph.
*/ 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 + '"');
} elseif (t in onStack) {
value = g.edge(e);
g.delEdge(e);
value.reversed = true;
++reverseCount;
g.addEdge(e, t, u, value);
} else {
dfs(t);
}
});
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 });
}
});
}
}
/* *Thisfunction"relaxes"theconstraintsappliedpreviouslybythe"apply" *function.Itexpandsanynodesthatwerecollapsedandassignstherankof *thecollapsednodetoeachoftheexpandednodes.Italsorestoresthe *originaledgesandremovesanydummyedgespointingatthecollapsednodes. * *Notethattheprocessofremovingcollapsednodesalsoremovesdummyedges *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);
}
});
},{}],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;
/* *Givenanacyclicgraphwitheachnodeassigneda`rank`attribute,this *functionconstructsandreturnsaspanningtree.Thisfunctionmayreduce *thelengthofsomeedgesfromtheinitialrankassignmentwhilemaintaining *the`minLen`specifiedbyeachedge. * *Prerequisites: * **Theinputgraphisacyclic **Eachnodeintheinputgraphhasanassigned`rank`attribute **Eachedgeintheinputgraphhasanassigned`minLen`attribute * *Outputs: * *Afeasiblespanningtreefortheinputgraph(i.e.aspanningtreethat *respectseachgraphedge's`minLen`attribute)representedasaDigraphwith *a`root`attributeongraph. * *Nodeshavethesameidandvalueasthatintheinputgraph. * *Edgesinthetreehavearbitrarilyassignedids.Theattributesforedges *include`reversed`.`reversed`indicatesthattheedgeisa *backedgeintheinputgraph.
*/ 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 });
}
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;
/* *Assignsa`rank`attributetoeachnodeintheinputgraphandensuresthat *thisrankrespectsthe`minLen`attributeofincidentedges. * *Prerequisites: * **Theinputgraphmustbeacyclic **Eachedgeintheinputgraphmusthaveanassigned'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;
}
},{}],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);
}
}
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);
}
/* *PerformaDFSpostordertraversal,labelingeachnodevwith *itstraversalorder'lim(v)'andtheminimumtraversalnumber *ofanyofitsdescendants'low(v)'.Thisprovidesanefficient *waytotestwhetheruisanancestorofvsince *low(u)<=lim(v)<=lim(u)ifandonlyifuisanancestor.
*/ 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);
}
/* *Tocomputethecutvalueoftheedgeparent->child,weconsider *itandanyothergraphedgestoorfromthechild. *parent *| *child */\ *uv
*/ 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;
}
/* *Returnwhethernisanodeinthesubtreewiththegiven *root.
*/ function inSubtree(tree, n, root) { return (tree.node(root).low <= tree.node(n).lim &&
tree.node(n).lim <= tree.node(root).lim);
}
/* *Returnanedgefromthetreewithanegativecutvalue,ornullifthere *isnone.
*/ 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;
}
} returnnull;
}
/* *Theedgeeshouldbeanedgeinthetree,withanunderlyingedge *inthegraph,withanegativecutvalue.Ofthetwonodesincident *ontheedge,takethelowerone.enterEdgereturnsanedgewith *minimumslackgoingfromoutsideofthatnode'ssubtreetoinside *ofthatnode'ssubtree.
*/ 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);
}
}); thrownew Error('No edge found from outside of tree to inside');
}
return minSlackEdge;
}
/* *Replaceedgeewithedgefinthetree,recalculatingthetreeroot, *thenodes'lowandlimpropertiesandtheedges'cutvalues.
*/ 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);
}
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;
}
};
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.addNode = function(u, value) { if (u === undefined || u === null) { do {
u = "_" + (++this._nextId);
} while (this.hasNode(u));
} elseif (this.hasNode(u)) { thrownew Error("Graph already has node '" + u + "'");
} this._nodes[u] = { id: u, value: value }; return 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));
} elseif (this.hasEdge(e)) { thrownew 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); deletethis._edges[e];
};
BaseGraph.prototype.filterNodes = function(filter) { var copy = newthis.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) { thrownew Error("Node '" + u + "' is not in graph");
} return node;
};
BaseGraph.prototype._strictGetEdge = function(e) { var edge = this._edges[e]; if (edge === undefined) { thrownew 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;
};
/* *Returnsallnodesinthegraphthathavenoin-edges.
*/
Digraph.prototype.sources = function() { var self = this; returnthis._filterNodes(function(u) { // This could have better space characteristics if we had an inDegree function. return self.inEdges(u).length === 0;
});
};
/* *Returnsallnodesinthegraphthathavenoout-edges.
*/
Digraph.prototype.sinks = function() { var self = this; returnthis._filterNodes(function(u) { // This could have better space characteristics if we have an outDegree function. return self.outEdges(u).length === 0;
});
};
},{"./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()) { thrownew Error("This function only works for undirected graphs");
} function dfs(u, prev) { if (visited.has(u)) { thrownew 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()) { thrownew Error("This function only works for undirected graphs");
} function dfs(u, prev) { if (visited.has(u)) { thrownew 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'salgorithm][]takesaconnectedundirectedgraphandgeneratesa *[minimumspanningtree][].Thisfunctionreturnstheminimumspanning *treeasanundirectedgraph.Thisalgorithmisderivedfromthedescription *in"IntroductiontoAlgorithms",ThirdEdition,Cormen,etal.,Pg634. * *Thisfunctiontakesa`weightFunc(e)`whichreturnstheweightoftheedge *`e`.ItthrowsanErrorifthegraphisnotconnected. * *Thisfunctiontakes`O(|E|log|V|)`time. * *[Prim'salgorithm]:https://en.wikipedia.org/wiki/Prim's_algorithm *[minimumspanningtree]:https://en.wikipedia.org/wiki/Minimum_spanning_tree * *@param{Graph}gthegraphusedtogeneratetheminimumspanningtree *@param{Function}weightFunctheweightfunctiontouse
*/ 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);
}
}
}
// 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]);
} elseif (init) { thrownew Error("Input graph is not connected: " + g);
} else {
init = true;
}
/** *Thisfunctionisanimplementationof[Tarjan'salgorithm][]whichfinds *all[stronglyconnectedcomponents][]inthedirectedgraph**g**.Each *stronglyconnectedcomponentiscomposedofnodesthatcanreachallother *nodesinthecomponentviadirectededges.Astronglyconnectedcomponent *canconsistofasinglenodeifthatnodecannotbothreachandbereached *byanyotherspecificnodeinthegraph.Componentsofmorethanonenode *areguaranteedtohaveatleastonecycle. * *Thisfunctionreturnsanarrayofcomponents.Eachcomponentisitselfan *arraythatcontainstheidsofallnodesinthecomponent. * *[Tarjan'salgorithm]:http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm *[stronglyconnectedcomponents]:http://en.wikipedia.org/wiki/Strongly_connected_component * *@param{Digraph}gthegraphtosearchforstronglyconnectedcomponents
*/ function tarjan(g) { if (!g.isDirected()) { thrownew 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);
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);
}
});
/* *Givenagraph**g**,thisfunctionreturnsanorderedlistofnodessuch *thatforeachedge`u->v`,`u`appearsbefore`v`inthelist.Ifthe *graphhasacycleitisimpossibletogeneratesuchalistand ***CycleException**isthrown. * *See[topologicalsorting](https://en.wikipedia.org/wiki/Topological_sorting) *formoredetailsabouthowthisalgorithmworks. * *@param{Digraph}gthegraphtosort
*/ function topsort(g) { if (!g.isDirected()) { thrownew 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) { thrownew 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) { thrownew 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.children = function(u) { if (u !== null) { this._strictGetNode(u);
} returnthis._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);
// 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);
});
}
});
}
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";
} elseif (graph instanceof CGraph) {
type = "cgraph";
} elseif (graph instanceof Digraph) {
type = "digraph";
} elseif (graph instanceof Graph) {
type = "graph";
} else { thrownew Error("Couldn't determine type of graph: " + graph);
}
return { nodes: nodes, edges: edges, type: type };
};
exports.nodesFromList = function(nodes) { var set = new Set(nodes); returnfunction(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.
/** *Returnsanewdirectedgraphusingthenodesandedgesfromthisgraph.The *newgraphwillhavethesamenodes,butwillhavetwicethenumberofedges: *eachedgeissplitintotwoedgeswithoppositedirections.Edgeids, *consequently,arenotpreservedbythistransformation.
*/
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;
};
/** *Returnsanewundirectedgraphusingthenodesandedgesfromthisgraph. *Thenewgraphwillhavethesamenodes,buttheedgeswillbemade *undirected.Edgeidsarepreservedinthistransformation.
*/
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;
};
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.160Bemerkung:
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.