feat(ui): add type filter toggles
This commit is contained in:
89
node_modules/dagre/lib/rank/feasible-tree.js
generated
vendored
Normal file
89
node_modules/dagre/lib/rank/feasible-tree.js
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
"use strict";
|
||||
|
||||
var _ = require("../lodash");
|
||||
var Graph = require("../graphlib").Graph;
|
||||
var slack = require("./util").slack;
|
||||
|
||||
module.exports = feasibleTree;
|
||||
|
||||
/*
|
||||
* Constructs a spanning tree with tight edges and adjusted the input node's
|
||||
* ranks to achieve this. A tight edge is one that is has a length that matches
|
||||
* its "minlen" attribute.
|
||||
*
|
||||
* The basic structure for this function is derived from Gansner, et al., "A
|
||||
* Technique for Drawing Directed Graphs."
|
||||
*
|
||||
* Pre-conditions:
|
||||
*
|
||||
* 1. Graph must be a DAG.
|
||||
* 2. Graph must be connected.
|
||||
* 3. Graph must have at least one node.
|
||||
* 5. Graph nodes must have been previously assigned a "rank" property that
|
||||
* respects the "minlen" property of incident edges.
|
||||
* 6. Graph edges must have a "minlen" property.
|
||||
*
|
||||
* Post-conditions:
|
||||
*
|
||||
* - Graph nodes will have their rank adjusted to ensure that all edges are
|
||||
* tight.
|
||||
*
|
||||
* Returns a tree (undirected graph) that is constructed using only "tight"
|
||||
* edges.
|
||||
*/
|
||||
function feasibleTree(g) {
|
||||
var t = new Graph({ directed: false });
|
||||
|
||||
// Choose arbitrary node from which to start our tree
|
||||
var start = g.nodes()[0];
|
||||
var size = g.nodeCount();
|
||||
t.setNode(start, {});
|
||||
|
||||
var edge, delta;
|
||||
while (tightTree(t, g) < size) {
|
||||
edge = findMinSlackEdge(t, g);
|
||||
delta = t.hasNode(edge.v) ? slack(g, edge) : -slack(g, edge);
|
||||
shiftRanks(t, g, delta);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds a maximal tree of tight edges and returns the number of nodes in the
|
||||
* tree.
|
||||
*/
|
||||
function tightTree(t, g) {
|
||||
function dfs(v) {
|
||||
_.forEach(g.nodeEdges(v), function(e) {
|
||||
var edgeV = e.v,
|
||||
w = (v === edgeV) ? e.w : edgeV;
|
||||
if (!t.hasNode(w) && !slack(g, e)) {
|
||||
t.setNode(w, {});
|
||||
t.setEdge(v, w, {});
|
||||
dfs(w);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_.forEach(t.nodes(), dfs);
|
||||
return t.nodeCount();
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the edge with the smallest slack that is incident on tree and returns
|
||||
* it.
|
||||
*/
|
||||
function findMinSlackEdge(t, g) {
|
||||
return _.minBy(g.edges(), function(e) {
|
||||
if (t.hasNode(e.v) !== t.hasNode(e.w)) {
|
||||
return slack(g, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function shiftRanks(t, g, delta) {
|
||||
_.forEach(t.nodes(), function(v) {
|
||||
g.node(v).rank += delta;
|
||||
});
|
||||
}
|
||||
48
node_modules/dagre/lib/rank/index.js
generated
vendored
Normal file
48
node_modules/dagre/lib/rank/index.js
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
"use strict";
|
||||
|
||||
var rankUtil = require("./util");
|
||||
var longestPath = rankUtil.longestPath;
|
||||
var feasibleTree = require("./feasible-tree");
|
||||
var networkSimplex = require("./network-simplex");
|
||||
|
||||
module.exports = rank;
|
||||
|
||||
/*
|
||||
* Assigns a rank to each node in the input graph that respects the "minlen"
|
||||
* constraint specified on edges between nodes.
|
||||
*
|
||||
* This basic structure is derived from Gansner, et al., "A Technique for
|
||||
* Drawing Directed Graphs."
|
||||
*
|
||||
* Pre-conditions:
|
||||
*
|
||||
* 1. Graph must be a connected DAG
|
||||
* 2. Graph nodes must be objects
|
||||
* 3. Graph edges must have "weight" and "minlen" attributes
|
||||
*
|
||||
* Post-conditions:
|
||||
*
|
||||
* 1. Graph nodes will have a "rank" attribute based on the results of the
|
||||
* algorithm. Ranks can start at any index (including negative), we'll
|
||||
* fix them up later.
|
||||
*/
|
||||
function rank(g) {
|
||||
switch(g.graph().ranker) {
|
||||
case "network-simplex": networkSimplexRanker(g); break;
|
||||
case "tight-tree": tightTreeRanker(g); break;
|
||||
case "longest-path": longestPathRanker(g); break;
|
||||
default: networkSimplexRanker(g);
|
||||
}
|
||||
}
|
||||
|
||||
// A fast and simple ranker, but results are far from optimal.
|
||||
var longestPathRanker = longestPath;
|
||||
|
||||
function tightTreeRanker(g) {
|
||||
longestPath(g);
|
||||
feasibleTree(g);
|
||||
}
|
||||
|
||||
function networkSimplexRanker(g) {
|
||||
networkSimplex(g);
|
||||
}
|
||||
234
node_modules/dagre/lib/rank/network-simplex.js
generated
vendored
Normal file
234
node_modules/dagre/lib/rank/network-simplex.js
generated
vendored
Normal file
@@ -0,0 +1,234 @@
|
||||
"use strict";
|
||||
|
||||
var _ = require("../lodash");
|
||||
var feasibleTree = require("./feasible-tree");
|
||||
var slack = require("./util").slack;
|
||||
var initRank = require("./util").longestPath;
|
||||
var preorder = require("../graphlib").alg.preorder;
|
||||
var postorder = require("../graphlib").alg.postorder;
|
||||
var simplify = require("../util").simplify;
|
||||
|
||||
module.exports = networkSimplex;
|
||||
|
||||
// Expose some internals for testing purposes
|
||||
networkSimplex.initLowLimValues = initLowLimValues;
|
||||
networkSimplex.initCutValues = initCutValues;
|
||||
networkSimplex.calcCutValue = calcCutValue;
|
||||
networkSimplex.leaveEdge = leaveEdge;
|
||||
networkSimplex.enterEdge = enterEdge;
|
||||
networkSimplex.exchangeEdges = exchangeEdges;
|
||||
|
||||
/*
|
||||
* The network simplex algorithm assigns ranks to each node in the input graph
|
||||
* and iteratively improves the ranking to reduce the length of edges.
|
||||
*
|
||||
* Preconditions:
|
||||
*
|
||||
* 1. The input graph must be a DAG.
|
||||
* 2. All nodes in the graph must have an object value.
|
||||
* 3. All edges in the graph must have "minlen" and "weight" attributes.
|
||||
*
|
||||
* Postconditions:
|
||||
*
|
||||
* 1. All nodes in the graph will have an assigned "rank" attribute that has
|
||||
* been optimized by the network simplex algorithm. Ranks start at 0.
|
||||
*
|
||||
*
|
||||
* A rough sketch of the algorithm is as follows:
|
||||
*
|
||||
* 1. Assign initial ranks to each node. We use the longest path algorithm,
|
||||
* which assigns ranks to the lowest position possible. In general this
|
||||
* leads to very wide bottom ranks and unnecessarily long edges.
|
||||
* 2. Construct a feasible tight tree. A tight tree is one such that all
|
||||
* edges in the tree have no slack (difference between length of edge
|
||||
* and minlen for the edge). This by itself greatly improves the assigned
|
||||
* rankings by shorting edges.
|
||||
* 3. Iteratively find edges that have negative cut values. Generally a
|
||||
* negative cut value indicates that the edge could be removed and a new
|
||||
* tree edge could be added to produce a more compact graph.
|
||||
*
|
||||
* Much of the algorithms here are derived from Gansner, et al., "A Technique
|
||||
* for Drawing Directed Graphs." The structure of the file roughly follows the
|
||||
* structure of the overall algorithm.
|
||||
*/
|
||||
function networkSimplex(g) {
|
||||
g = simplify(g);
|
||||
initRank(g);
|
||||
var t = feasibleTree(g);
|
||||
initLowLimValues(t);
|
||||
initCutValues(t, g);
|
||||
|
||||
var e, f;
|
||||
while ((e = leaveEdge(t))) {
|
||||
f = enterEdge(t, g, e);
|
||||
exchangeEdges(t, g, e, f);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initializes cut values for all edges in the tree.
|
||||
*/
|
||||
function initCutValues(t, g) {
|
||||
var vs = postorder(t, t.nodes());
|
||||
vs = vs.slice(0, vs.length - 1);
|
||||
_.forEach(vs, function(v) {
|
||||
assignCutValue(t, g, v);
|
||||
});
|
||||
}
|
||||
|
||||
function assignCutValue(t, g, child) {
|
||||
var childLab = t.node(child);
|
||||
var parent = childLab.parent;
|
||||
t.edge(child, parent).cutvalue = calcCutValue(t, g, child);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given the tight tree, its graph, and a child in the graph calculate and
|
||||
* return the cut value for the edge between the child and its parent.
|
||||
*/
|
||||
function calcCutValue(t, g, child) {
|
||||
var childLab = t.node(child);
|
||||
var parent = childLab.parent;
|
||||
// True if the child is on the tail end of the edge in the directed graph
|
||||
var childIsTail = true;
|
||||
// The graph's view of the tree edge we're inspecting
|
||||
var graphEdge = g.edge(child, parent);
|
||||
// The accumulated cut value for the edge between this node and its parent
|
||||
var cutValue = 0;
|
||||
|
||||
if (!graphEdge) {
|
||||
childIsTail = false;
|
||||
graphEdge = g.edge(parent, child);
|
||||
}
|
||||
|
||||
cutValue = graphEdge.weight;
|
||||
|
||||
_.forEach(g.nodeEdges(child), function(e) {
|
||||
var isOutEdge = e.v === child,
|
||||
other = isOutEdge ? e.w : e.v;
|
||||
|
||||
if (other !== parent) {
|
||||
var pointsToHead = isOutEdge === childIsTail,
|
||||
otherWeight = g.edge(e).weight;
|
||||
|
||||
cutValue += pointsToHead ? otherWeight : -otherWeight;
|
||||
if (isTreeEdge(t, child, other)) {
|
||||
var otherCutValue = t.edge(child, other).cutvalue;
|
||||
cutValue += pointsToHead ? -otherCutValue : otherCutValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return cutValue;
|
||||
}
|
||||
|
||||
function initLowLimValues(tree, root) {
|
||||
if (arguments.length < 2) {
|
||||
root = tree.nodes()[0];
|
||||
}
|
||||
dfsAssignLowLim(tree, {}, 1, root);
|
||||
}
|
||||
|
||||
function dfsAssignLowLim(tree, visited, nextLim, v, parent) {
|
||||
var low = nextLim;
|
||||
var label = tree.node(v);
|
||||
|
||||
visited[v] = true;
|
||||
_.forEach(tree.neighbors(v), function(w) {
|
||||
if (!_.has(visited, w)) {
|
||||
nextLim = dfsAssignLowLim(tree, visited, nextLim, w, v);
|
||||
}
|
||||
});
|
||||
|
||||
label.low = low;
|
||||
label.lim = nextLim++;
|
||||
if (parent) {
|
||||
label.parent = parent;
|
||||
} else {
|
||||
// TODO should be able to remove this when we incrementally update low lim
|
||||
delete label.parent;
|
||||
}
|
||||
|
||||
return nextLim;
|
||||
}
|
||||
|
||||
function leaveEdge(tree) {
|
||||
return _.find(tree.edges(), function(e) {
|
||||
return tree.edge(e).cutvalue < 0;
|
||||
});
|
||||
}
|
||||
|
||||
function enterEdge(t, g, edge) {
|
||||
var v = edge.v;
|
||||
var w = edge.w;
|
||||
|
||||
// For the rest of this function we assume that v is the tail and w is the
|
||||
// head, so if we don't have this edge in the graph we should flip it to
|
||||
// match the correct orientation.
|
||||
if (!g.hasEdge(v, w)) {
|
||||
v = edge.w;
|
||||
w = edge.v;
|
||||
}
|
||||
|
||||
var vLabel = t.node(v);
|
||||
var wLabel = t.node(w);
|
||||
var tailLabel = vLabel;
|
||||
var flip = false;
|
||||
|
||||
// If the root is in the tail of the edge then we need to flip the logic that
|
||||
// checks for the head and tail nodes in the candidates function below.
|
||||
if (vLabel.lim > wLabel.lim) {
|
||||
tailLabel = wLabel;
|
||||
flip = true;
|
||||
}
|
||||
|
||||
var candidates = _.filter(g.edges(), function(edge) {
|
||||
return flip === isDescendant(t, t.node(edge.v), tailLabel) &&
|
||||
flip !== isDescendant(t, t.node(edge.w), tailLabel);
|
||||
});
|
||||
|
||||
return _.minBy(candidates, function(edge) { return slack(g, edge); });
|
||||
}
|
||||
|
||||
function exchangeEdges(t, g, e, f) {
|
||||
var v = e.v;
|
||||
var w = e.w;
|
||||
t.removeEdge(v, w);
|
||||
t.setEdge(f.v, f.w, {});
|
||||
initLowLimValues(t);
|
||||
initCutValues(t, g);
|
||||
updateRanks(t, g);
|
||||
}
|
||||
|
||||
function updateRanks(t, g) {
|
||||
var root = _.find(t.nodes(), function(v) { return !g.node(v).parent; });
|
||||
var vs = preorder(t, root);
|
||||
vs = vs.slice(1);
|
||||
_.forEach(vs, function(v) {
|
||||
var parent = t.node(v).parent,
|
||||
edge = g.edge(v, parent),
|
||||
flipped = false;
|
||||
|
||||
if (!edge) {
|
||||
edge = g.edge(parent, v);
|
||||
flipped = true;
|
||||
}
|
||||
|
||||
g.node(v).rank = g.node(parent).rank + (flipped ? edge.minlen : -edge.minlen);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the edge is in the tree.
|
||||
*/
|
||||
function isTreeEdge(tree, u, v) {
|
||||
return tree.hasEdge(u, v);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the specified node is descendant of the root node per the
|
||||
* assigned low and lim attributes in the tree.
|
||||
*/
|
||||
function isDescendant(tree, vLabel, rootLabel) {
|
||||
return rootLabel.low <= vLabel.lim && vLabel.lim <= rootLabel.lim;
|
||||
}
|
||||
63
node_modules/dagre/lib/rank/util.js
generated
vendored
Normal file
63
node_modules/dagre/lib/rank/util.js
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
"use strict";
|
||||
|
||||
var _ = require("../lodash");
|
||||
|
||||
module.exports = {
|
||||
longestPath: longestPath,
|
||||
slack: slack
|
||||
};
|
||||
|
||||
/*
|
||||
* Initializes ranks for the input graph using the longest path algorithm. This
|
||||
* algorithm scales well and is fast in practice, it yields rather poor
|
||||
* solutions. Nodes are pushed to the lowest layer possible, leaving the bottom
|
||||
* ranks wide and leaving edges longer than necessary. However, due to its
|
||||
* speed, this algorithm is good for getting an initial ranking that can be fed
|
||||
* into other algorithms.
|
||||
*
|
||||
* This algorithm does not normalize layers because it will be used by other
|
||||
* algorithms in most cases. If using this algorithm directly, be sure to
|
||||
* run normalize at the end.
|
||||
*
|
||||
* Pre-conditions:
|
||||
*
|
||||
* 1. Input graph is a DAG.
|
||||
* 2. Input graph node labels can be assigned properties.
|
||||
*
|
||||
* Post-conditions:
|
||||
*
|
||||
* 1. Each node will be assign an (unnormalized) "rank" property.
|
||||
*/
|
||||
function longestPath(g) {
|
||||
var visited = {};
|
||||
|
||||
function dfs(v) {
|
||||
var label = g.node(v);
|
||||
if (_.has(visited, v)) {
|
||||
return label.rank;
|
||||
}
|
||||
visited[v] = true;
|
||||
|
||||
var rank = _.min(_.map(g.outEdges(v), function(e) {
|
||||
return dfs(e.w) - g.edge(e).minlen;
|
||||
}));
|
||||
|
||||
if (rank === Number.POSITIVE_INFINITY || // return value of _.map([]) for Lodash 3
|
||||
rank === undefined || // return value of _.map([]) for Lodash 4
|
||||
rank === null) { // return value of _.map([null])
|
||||
rank = 0;
|
||||
}
|
||||
|
||||
return (label.rank = rank);
|
||||
}
|
||||
|
||||
_.forEach(g.sources(), dfs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the amount of slack for the given edge. The slack is defined as the
|
||||
* difference between the length of the edge and its minimum length.
|
||||
*/
|
||||
function slack(g, e) {
|
||||
return g.node(e.w).rank - g.node(e.v).rank - g.edge(e).minlen;
|
||||
}
|
||||
Reference in New Issue
Block a user