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