@dagrejs/dagre 1.1.8 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dagre.cjs.js +3 -0
- package/{index.js → dist/dagre.cjs.js.LEGAL.txt} +2 -12
- package/dist/dagre.cjs.js.map +7 -0
- package/dist/dagre.d.ts +104 -0
- package/dist/dagre.esm.js +3 -0
- package/dist/dagre.esm.js.LEGAL.txt +23 -0
- package/dist/dagre.esm.js.map +7 -0
- package/dist/dagre.js +2183 -4321
- package/dist/dagre.js.LEGAL.txt +23 -0
- package/dist/dagre.js.map +7 -0
- package/dist/dagre.min.js +3 -815
- package/dist/dagre.min.js.LEGAL.txt +23 -0
- package/dist/dagre.min.js.map +7 -0
- package/package.json +20 -24
- package/index.d.ts +0 -147
- package/lib/acyclic.js +0 -67
- package/lib/add-border-segments.js +0 -37
- package/lib/coordinate-system.js +0 -70
- package/lib/data/list.js +0 -58
- package/lib/debug.js +0 -31
- package/lib/greedy-fas.js +0 -124
- package/lib/layout.js +0 -405
- package/lib/nesting-graph.js +0 -126
- package/lib/normalize.js +0 -89
- package/lib/order/add-subgraph-constraints.js +0 -51
- package/lib/order/barycenter.js +0 -26
- package/lib/order/build-layer-graph.js +0 -79
- package/lib/order/cross-count.js +0 -66
- package/lib/order/index.js +0 -111
- package/lib/order/init-order.js +0 -37
- package/lib/order/resolve-conflicts.js +0 -118
- package/lib/order/sort-subgraph.js +0 -73
- package/lib/order/sort.js +0 -56
- package/lib/parent-dummy-chains.js +0 -84
- package/lib/position/bk.js +0 -424
- package/lib/position/index.js +0 -32
- package/lib/rank/feasible-tree.js +0 -95
- package/lib/rank/index.js +0 -54
- package/lib/rank/network-simplex.js +0 -235
- package/lib/rank/util.js +0 -67
- package/lib/util.js +0 -331
- package/lib/version.js +0 -1
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
let Graph = require("@dagrejs/graphlib").Graph;
|
|
2
|
-
let util = require("../util");
|
|
3
|
-
|
|
4
|
-
module.exports = buildLayerGraph;
|
|
5
|
-
|
|
6
|
-
/*
|
|
7
|
-
* Constructs a graph that can be used to sort a layer of nodes. The graph will
|
|
8
|
-
* contain all base and subgraph nodes from the request layer in their original
|
|
9
|
-
* hierarchy and any edges that are incident on these nodes and are of the type
|
|
10
|
-
* requested by the "relationship" parameter.
|
|
11
|
-
*
|
|
12
|
-
* Nodes from the requested rank that do not have parents are assigned a root
|
|
13
|
-
* node in the output graph, which is set in the root graph attribute. This
|
|
14
|
-
* makes it easy to walk the hierarchy of movable nodes during ordering.
|
|
15
|
-
*
|
|
16
|
-
* Pre-conditions:
|
|
17
|
-
*
|
|
18
|
-
* 1. Input graph is a DAG
|
|
19
|
-
* 2. Base nodes in the input graph have a rank attribute
|
|
20
|
-
* 3. Subgraph nodes in the input graph has minRank and maxRank attributes
|
|
21
|
-
* 4. Edges have an assigned weight
|
|
22
|
-
* 5. If `nodesWithRank` is not undefined, it must contains only the nodes
|
|
23
|
-
* which belong to `g` and belong to `rank`.
|
|
24
|
-
*
|
|
25
|
-
* Post-conditions:
|
|
26
|
-
*
|
|
27
|
-
* 1. Output graph has all nodes in the movable rank with preserved
|
|
28
|
-
* hierarchy.
|
|
29
|
-
* 2. Root nodes in the movable layer are made children of the node
|
|
30
|
-
* indicated by the root attribute of the graph.
|
|
31
|
-
* 3. Non-movable nodes incident on movable nodes, selected by the
|
|
32
|
-
* relationship parameter, are included in the graph (without hierarchy).
|
|
33
|
-
* 4. Edges incident on movable nodes, selected by the relationship
|
|
34
|
-
* parameter, are added to the output graph.
|
|
35
|
-
* 5. The weights for copied edges are aggregated as need, since the output
|
|
36
|
-
* graph is not a multi-graph.
|
|
37
|
-
*/
|
|
38
|
-
function buildLayerGraph(g, rank, relationship, nodesWithRank) {
|
|
39
|
-
if (!nodesWithRank) {
|
|
40
|
-
nodesWithRank = g.nodes();
|
|
41
|
-
}
|
|
42
|
-
let root = createRootNode(g),
|
|
43
|
-
result = new Graph({ compound: true })
|
|
44
|
-
.setGraph({ root: root })
|
|
45
|
-
.setDefaultNodeLabel((v) => g.node(v));
|
|
46
|
-
|
|
47
|
-
nodesWithRank.forEach((v) => {
|
|
48
|
-
let node = g.node(v),
|
|
49
|
-
parent = g.parent(v);
|
|
50
|
-
|
|
51
|
-
if (node.rank === rank || node.minRank <= rank && rank <= node.maxRank) {
|
|
52
|
-
result.setNode(v);
|
|
53
|
-
result.setParent(v, parent || root);
|
|
54
|
-
|
|
55
|
-
// This assumes we have only short edges!
|
|
56
|
-
g[relationship](v).forEach(e => {
|
|
57
|
-
let u = e.v === v ? e.w : e.v,
|
|
58
|
-
edge = result.edge(u, v),
|
|
59
|
-
weight = edge !== undefined ? edge.weight : 0;
|
|
60
|
-
result.setEdge(u, v, { weight: g.edge(e).weight + weight });
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
if (Object.hasOwn(node, "minRank")) {
|
|
64
|
-
result.setNode(v, {
|
|
65
|
-
borderLeft: node.borderLeft[rank],
|
|
66
|
-
borderRight: node.borderRight[rank]
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
return result;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function createRootNode(g) {
|
|
76
|
-
var v;
|
|
77
|
-
while (g.hasNode((v = util.uniqueId("_root"))));
|
|
78
|
-
return v;
|
|
79
|
-
}
|
package/lib/order/cross-count.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
let zipObject = require("../util").zipObject;
|
|
4
|
-
|
|
5
|
-
module.exports = crossCount;
|
|
6
|
-
|
|
7
|
-
/*
|
|
8
|
-
* A function that takes a layering (an array of layers, each with an array of
|
|
9
|
-
* ordererd nodes) and a graph and returns a weighted crossing count.
|
|
10
|
-
*
|
|
11
|
-
* Pre-conditions:
|
|
12
|
-
*
|
|
13
|
-
* 1. Input graph must be simple (not a multigraph), directed, and include
|
|
14
|
-
* only simple edges.
|
|
15
|
-
* 2. Edges in the input graph must have assigned weights.
|
|
16
|
-
*
|
|
17
|
-
* Post-conditions:
|
|
18
|
-
*
|
|
19
|
-
* 1. The graph and layering matrix are left unchanged.
|
|
20
|
-
*
|
|
21
|
-
* This algorithm is derived from Barth, et al., "Bilayer Cross Counting."
|
|
22
|
-
*/
|
|
23
|
-
function crossCount(g, layering) {
|
|
24
|
-
let cc = 0;
|
|
25
|
-
for (let i = 1; i < layering.length; ++i) {
|
|
26
|
-
cc += twoLayerCrossCount(g, layering[i-1], layering[i]);
|
|
27
|
-
}
|
|
28
|
-
return cc;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function twoLayerCrossCount(g, northLayer, southLayer) {
|
|
32
|
-
// Sort all of the edges between the north and south layers by their position
|
|
33
|
-
// in the north layer and then the south. Map these edges to the position of
|
|
34
|
-
// their head in the south layer.
|
|
35
|
-
let southPos = zipObject(southLayer, southLayer.map((v, i) => i));
|
|
36
|
-
let southEntries = northLayer.flatMap(v => {
|
|
37
|
-
return g.outEdges(v).map(e => {
|
|
38
|
-
return { pos: southPos[e.w], weight: g.edge(e).weight };
|
|
39
|
-
}).sort((a, b) => a.pos - b.pos);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
// Build the accumulator tree
|
|
43
|
-
let firstIndex = 1;
|
|
44
|
-
while (firstIndex < southLayer.length) firstIndex <<= 1;
|
|
45
|
-
let treeSize = 2 * firstIndex - 1;
|
|
46
|
-
firstIndex -= 1;
|
|
47
|
-
let tree = new Array(treeSize).fill(0);
|
|
48
|
-
|
|
49
|
-
// Calculate the weighted crossings
|
|
50
|
-
let cc = 0;
|
|
51
|
-
southEntries.forEach(entry => {
|
|
52
|
-
let index = entry.pos + firstIndex;
|
|
53
|
-
tree[index] += entry.weight;
|
|
54
|
-
let weightSum = 0;
|
|
55
|
-
while (index > 0) {
|
|
56
|
-
if (index % 2) {
|
|
57
|
-
weightSum += tree[index + 1];
|
|
58
|
-
}
|
|
59
|
-
index = (index - 1) >> 1;
|
|
60
|
-
tree[index] += entry.weight;
|
|
61
|
-
}
|
|
62
|
-
cc += entry.weight * weightSum;
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
return cc;
|
|
66
|
-
}
|
package/lib/order/index.js
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
let initOrder = require("./init-order");
|
|
4
|
-
let crossCount = require("./cross-count");
|
|
5
|
-
let sortSubgraph = require("./sort-subgraph");
|
|
6
|
-
let buildLayerGraph = require("./build-layer-graph");
|
|
7
|
-
let addSubgraphConstraints = require("./add-subgraph-constraints");
|
|
8
|
-
let Graph = require("@dagrejs/graphlib").Graph;
|
|
9
|
-
let util = require("../util");
|
|
10
|
-
|
|
11
|
-
module.exports = order;
|
|
12
|
-
|
|
13
|
-
/*
|
|
14
|
-
* Applies heuristics to minimize edge crossings in the graph and sets the best
|
|
15
|
-
* order solution as an order attribute on each node.
|
|
16
|
-
*
|
|
17
|
-
* Pre-conditions:
|
|
18
|
-
*
|
|
19
|
-
* 1. Graph must be DAG
|
|
20
|
-
* 2. Graph nodes must be objects with a "rank" attribute
|
|
21
|
-
* 3. Graph edges must have the "weight" attribute
|
|
22
|
-
*
|
|
23
|
-
* Post-conditions:
|
|
24
|
-
*
|
|
25
|
-
* 1. Graph nodes will have an "order" attribute based on the results of the
|
|
26
|
-
* algorithm.
|
|
27
|
-
*/
|
|
28
|
-
function order(g, opts) {
|
|
29
|
-
if (opts && typeof opts.customOrder === 'function') {
|
|
30
|
-
opts.customOrder(g, order);
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
let maxRank = util.maxRank(g),
|
|
35
|
-
downLayerGraphs = buildLayerGraphs(g, util.range(1, maxRank + 1), "inEdges"),
|
|
36
|
-
upLayerGraphs = buildLayerGraphs(g, util.range(maxRank - 1, -1, -1), "outEdges");
|
|
37
|
-
|
|
38
|
-
let layering = initOrder(g);
|
|
39
|
-
assignOrder(g, layering);
|
|
40
|
-
|
|
41
|
-
if (opts && opts.disableOptimalOrderHeuristic) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
let bestCC = Number.POSITIVE_INFINITY,
|
|
46
|
-
best;
|
|
47
|
-
|
|
48
|
-
for (let i = 0, lastBest = 0; lastBest < 4; ++i, ++lastBest) {
|
|
49
|
-
sweepLayerGraphs(i % 2 ? downLayerGraphs : upLayerGraphs, i % 4 >= 2);
|
|
50
|
-
|
|
51
|
-
layering = util.buildLayerMatrix(g);
|
|
52
|
-
let cc = crossCount(g, layering);
|
|
53
|
-
if (cc < bestCC) {
|
|
54
|
-
lastBest = 0;
|
|
55
|
-
best = Object.assign({}, layering);
|
|
56
|
-
bestCC = cc;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
assignOrder(g, best);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function buildLayerGraphs(g, ranks, relationship) {
|
|
64
|
-
// Build an index mapping from rank to the nodes with that rank.
|
|
65
|
-
// This helps to avoid a quadratic search for all nodes with the same rank as
|
|
66
|
-
// the current node.
|
|
67
|
-
const nodesByRank = new Map();
|
|
68
|
-
const addNodeToRank = (rank, node) => {
|
|
69
|
-
if (!nodesByRank.has(rank)) {
|
|
70
|
-
nodesByRank.set(rank, []);
|
|
71
|
-
}
|
|
72
|
-
nodesByRank.get(rank).push(node);
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
// Visit the nodes in their original order in the graph, and add each
|
|
76
|
-
// node to the ranks(s) that it belongs to.
|
|
77
|
-
for (const v of g.nodes()) {
|
|
78
|
-
const node = g.node(v);
|
|
79
|
-
if (typeof node.rank === "number") {
|
|
80
|
-
addNodeToRank(node.rank, v);
|
|
81
|
-
}
|
|
82
|
-
// If there is a range of ranks, add it to each, but skip the `node.rank` which
|
|
83
|
-
// has already had the node added.
|
|
84
|
-
if (typeof node.minRank === "number" && typeof node.maxRank === "number") {
|
|
85
|
-
for (let r = node.minRank; r <= node.maxRank; r++) {
|
|
86
|
-
if (r !== node.rank) {
|
|
87
|
-
// Don't add this node to its `node.rank` twice.
|
|
88
|
-
addNodeToRank(r, v);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return ranks.map(function (rank) {
|
|
95
|
-
return buildLayerGraph(g, rank, relationship, nodesByRank.get(rank) || []);
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function sweepLayerGraphs(layerGraphs, biasRight) {
|
|
100
|
-
let cg = new Graph();
|
|
101
|
-
layerGraphs.forEach(function(lg) {
|
|
102
|
-
let root = lg.graph().root;
|
|
103
|
-
let sorted = sortSubgraph(lg, root, cg, biasRight);
|
|
104
|
-
sorted.vs.forEach((v, i) => lg.node(v).order = i);
|
|
105
|
-
addSubgraphConstraints(lg, cg, sorted.vs);
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function assignOrder(g, layering) {
|
|
110
|
-
Object.values(layering).forEach(layer => layer.forEach((v, i) => g.node(v).order = i));
|
|
111
|
-
}
|
package/lib/order/init-order.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
let util = require("../util");
|
|
4
|
-
|
|
5
|
-
module.exports = initOrder;
|
|
6
|
-
|
|
7
|
-
/*
|
|
8
|
-
* Assigns an initial order value for each node by performing a DFS search
|
|
9
|
-
* starting from nodes in the first rank. Nodes are assigned an order in their
|
|
10
|
-
* rank as they are first visited.
|
|
11
|
-
*
|
|
12
|
-
* This approach comes from Gansner, et al., "A Technique for Drawing Directed
|
|
13
|
-
* Graphs."
|
|
14
|
-
*
|
|
15
|
-
* Returns a layering matrix with an array per layer and each layer sorted by
|
|
16
|
-
* the order of its nodes.
|
|
17
|
-
*/
|
|
18
|
-
function initOrder(g) {
|
|
19
|
-
let visited = {};
|
|
20
|
-
let simpleNodes = g.nodes().filter(v => !g.children(v).length);
|
|
21
|
-
let simpleNodesRanks = simpleNodes.map(v => g.node(v).rank);
|
|
22
|
-
let maxRank = util.applyWithChunking(Math.max, simpleNodesRanks);
|
|
23
|
-
let layers = util.range(maxRank + 1).map(() => []);
|
|
24
|
-
|
|
25
|
-
function dfs(v) {
|
|
26
|
-
if (visited[v]) return;
|
|
27
|
-
visited[v] = true;
|
|
28
|
-
let node = g.node(v);
|
|
29
|
-
layers[node.rank].push(v);
|
|
30
|
-
g.successors(v).forEach(dfs);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
let orderedVs = simpleNodes.sort((a, b) => g.node(a).rank - g.node(b).rank);
|
|
34
|
-
orderedVs.forEach(dfs);
|
|
35
|
-
|
|
36
|
-
return layers;
|
|
37
|
-
}
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
let util = require("../util");
|
|
4
|
-
|
|
5
|
-
module.exports = resolveConflicts;
|
|
6
|
-
|
|
7
|
-
/*
|
|
8
|
-
* Given a list of entries of the form {v, barycenter, weight} and a
|
|
9
|
-
* constraint graph this function will resolve any conflicts between the
|
|
10
|
-
* constraint graph and the barycenters for the entries. If the barycenters for
|
|
11
|
-
* an entry would violate a constraint in the constraint graph then we coalesce
|
|
12
|
-
* the nodes in the conflict into a new node that respects the contraint and
|
|
13
|
-
* aggregates barycenter and weight information.
|
|
14
|
-
*
|
|
15
|
-
* This implementation is based on the description in Forster, "A Fast and
|
|
16
|
-
* Simple Hueristic for Constrained Two-Level Crossing Reduction," thought it
|
|
17
|
-
* differs in some specific details.
|
|
18
|
-
*
|
|
19
|
-
* Pre-conditions:
|
|
20
|
-
*
|
|
21
|
-
* 1. Each entry has the form {v, barycenter, weight}, or if the node has
|
|
22
|
-
* no barycenter, then {v}.
|
|
23
|
-
*
|
|
24
|
-
* Returns:
|
|
25
|
-
*
|
|
26
|
-
* A new list of entries of the form {vs, i, barycenter, weight}. The list
|
|
27
|
-
* `vs` may either be a singleton or it may be an aggregation of nodes
|
|
28
|
-
* ordered such that they do not violate constraints from the constraint
|
|
29
|
-
* graph. The property `i` is the lowest original index of any of the
|
|
30
|
-
* elements in `vs`.
|
|
31
|
-
*/
|
|
32
|
-
function resolveConflicts(entries, cg) {
|
|
33
|
-
let mappedEntries = {};
|
|
34
|
-
entries.forEach((entry, i) => {
|
|
35
|
-
let tmp = mappedEntries[entry.v] = {
|
|
36
|
-
indegree: 0,
|
|
37
|
-
"in": [],
|
|
38
|
-
out: [],
|
|
39
|
-
vs: [entry.v],
|
|
40
|
-
i: i
|
|
41
|
-
};
|
|
42
|
-
if (entry.barycenter !== undefined) {
|
|
43
|
-
tmp.barycenter = entry.barycenter;
|
|
44
|
-
tmp.weight = entry.weight;
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
cg.edges().forEach(e => {
|
|
49
|
-
let entryV = mappedEntries[e.v];
|
|
50
|
-
let entryW = mappedEntries[e.w];
|
|
51
|
-
if (entryV !== undefined && entryW !== undefined) {
|
|
52
|
-
entryW.indegree++;
|
|
53
|
-
entryV.out.push(mappedEntries[e.w]);
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
let sourceSet = Object.values(mappedEntries).filter(entry => !entry.indegree);
|
|
58
|
-
|
|
59
|
-
return doResolveConflicts(sourceSet);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function doResolveConflicts(sourceSet) {
|
|
63
|
-
let entries = [];
|
|
64
|
-
|
|
65
|
-
function handleIn(vEntry) {
|
|
66
|
-
return uEntry => {
|
|
67
|
-
if (uEntry.merged) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
if (uEntry.barycenter === undefined ||
|
|
71
|
-
vEntry.barycenter === undefined ||
|
|
72
|
-
uEntry.barycenter >= vEntry.barycenter) {
|
|
73
|
-
mergeEntries(vEntry, uEntry);
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function handleOut(vEntry) {
|
|
79
|
-
return wEntry => {
|
|
80
|
-
wEntry["in"].push(vEntry);
|
|
81
|
-
if (--wEntry.indegree === 0) {
|
|
82
|
-
sourceSet.push(wEntry);
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
while (sourceSet.length) {
|
|
88
|
-
let entry = sourceSet.pop();
|
|
89
|
-
entries.push(entry);
|
|
90
|
-
entry["in"].reverse().forEach(handleIn(entry));
|
|
91
|
-
entry.out.forEach(handleOut(entry));
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return entries.filter(entry => !entry.merged).map(entry => {
|
|
95
|
-
return util.pick(entry, ["vs", "i", "barycenter", "weight"]);
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function mergeEntries(target, source) {
|
|
100
|
-
let sum = 0;
|
|
101
|
-
let weight = 0;
|
|
102
|
-
|
|
103
|
-
if (target.weight) {
|
|
104
|
-
sum += target.barycenter * target.weight;
|
|
105
|
-
weight += target.weight;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (source.weight) {
|
|
109
|
-
sum += source.barycenter * source.weight;
|
|
110
|
-
weight += source.weight;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
target.vs = source.vs.concat(target.vs);
|
|
114
|
-
target.barycenter = sum / weight;
|
|
115
|
-
target.weight = weight;
|
|
116
|
-
target.i = Math.min(source.i, target.i);
|
|
117
|
-
source.merged = true;
|
|
118
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
let barycenter = require("./barycenter");
|
|
2
|
-
let resolveConflicts = require("./resolve-conflicts");
|
|
3
|
-
let sort = require("./sort");
|
|
4
|
-
|
|
5
|
-
module.exports = sortSubgraph;
|
|
6
|
-
|
|
7
|
-
function sortSubgraph(g, v, cg, biasRight) {
|
|
8
|
-
let movable = g.children(v);
|
|
9
|
-
let node = g.node(v);
|
|
10
|
-
let bl = node ? node.borderLeft : undefined;
|
|
11
|
-
let br = node ? node.borderRight: undefined;
|
|
12
|
-
let subgraphs = {};
|
|
13
|
-
|
|
14
|
-
if (bl) {
|
|
15
|
-
movable = movable.filter(w => w !== bl && w !== br);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
let barycenters = barycenter(g, movable);
|
|
19
|
-
barycenters.forEach(entry => {
|
|
20
|
-
if (g.children(entry.v).length) {
|
|
21
|
-
let subgraphResult = sortSubgraph(g, entry.v, cg, biasRight);
|
|
22
|
-
subgraphs[entry.v] = subgraphResult;
|
|
23
|
-
if (Object.hasOwn(subgraphResult, "barycenter")) {
|
|
24
|
-
mergeBarycenters(entry, subgraphResult);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
let entries = resolveConflicts(barycenters, cg);
|
|
30
|
-
expandSubgraphs(entries, subgraphs);
|
|
31
|
-
|
|
32
|
-
let result = sort(entries, biasRight);
|
|
33
|
-
|
|
34
|
-
if (bl) {
|
|
35
|
-
result.vs = [bl, result.vs, br].flat(true);
|
|
36
|
-
if (g.predecessors(bl).length) {
|
|
37
|
-
let blPred = g.node(g.predecessors(bl)[0]),
|
|
38
|
-
brPred = g.node(g.predecessors(br)[0]);
|
|
39
|
-
if (!Object.hasOwn(result, "barycenter")) {
|
|
40
|
-
result.barycenter = 0;
|
|
41
|
-
result.weight = 0;
|
|
42
|
-
}
|
|
43
|
-
result.barycenter = (result.barycenter * result.weight +
|
|
44
|
-
blPred.order + brPred.order) / (result.weight + 2);
|
|
45
|
-
result.weight += 2;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return result;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function expandSubgraphs(entries, subgraphs) {
|
|
53
|
-
entries.forEach(entry => {
|
|
54
|
-
entry.vs = entry.vs.flatMap(v => {
|
|
55
|
-
if (subgraphs[v]) {
|
|
56
|
-
return subgraphs[v].vs;
|
|
57
|
-
}
|
|
58
|
-
return v;
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function mergeBarycenters(target, other) {
|
|
64
|
-
if (target.barycenter !== undefined) {
|
|
65
|
-
target.barycenter = (target.barycenter * target.weight +
|
|
66
|
-
other.barycenter * other.weight) /
|
|
67
|
-
(target.weight + other.weight);
|
|
68
|
-
target.weight += other.weight;
|
|
69
|
-
} else {
|
|
70
|
-
target.barycenter = other.barycenter;
|
|
71
|
-
target.weight = other.weight;
|
|
72
|
-
}
|
|
73
|
-
}
|
package/lib/order/sort.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
let util = require("../util");
|
|
2
|
-
|
|
3
|
-
module.exports = sort;
|
|
4
|
-
|
|
5
|
-
function sort(entries, biasRight) {
|
|
6
|
-
let parts = util.partition(entries, entry => {
|
|
7
|
-
return Object.hasOwn(entry, "barycenter");
|
|
8
|
-
});
|
|
9
|
-
let sortable = parts.lhs,
|
|
10
|
-
unsortable = parts.rhs.sort((a, b) => b.i - a.i),
|
|
11
|
-
vs = [],
|
|
12
|
-
sum = 0,
|
|
13
|
-
weight = 0,
|
|
14
|
-
vsIndex = 0;
|
|
15
|
-
|
|
16
|
-
sortable.sort(compareWithBias(!!biasRight));
|
|
17
|
-
|
|
18
|
-
vsIndex = consumeUnsortable(vs, unsortable, vsIndex);
|
|
19
|
-
|
|
20
|
-
sortable.forEach(entry => {
|
|
21
|
-
vsIndex += entry.vs.length;
|
|
22
|
-
vs.push(entry.vs);
|
|
23
|
-
sum += entry.barycenter * entry.weight;
|
|
24
|
-
weight += entry.weight;
|
|
25
|
-
vsIndex = consumeUnsortable(vs, unsortable, vsIndex);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
let result = { vs: vs.flat(true) };
|
|
29
|
-
if (weight) {
|
|
30
|
-
result.barycenter = sum / weight;
|
|
31
|
-
result.weight = weight;
|
|
32
|
-
}
|
|
33
|
-
return result;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function consumeUnsortable(vs, unsortable, index) {
|
|
37
|
-
let last;
|
|
38
|
-
while (unsortable.length && (last = unsortable[unsortable.length - 1]).i <= index) {
|
|
39
|
-
unsortable.pop();
|
|
40
|
-
vs.push(last.vs);
|
|
41
|
-
index++;
|
|
42
|
-
}
|
|
43
|
-
return index;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function compareWithBias(bias) {
|
|
47
|
-
return (entryV, entryW) => {
|
|
48
|
-
if (entryV.barycenter < entryW.barycenter) {
|
|
49
|
-
return -1;
|
|
50
|
-
} else if (entryV.barycenter > entryW.barycenter) {
|
|
51
|
-
return 1;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return !bias ? entryV.i - entryW.i : entryW.i - entryV.i;
|
|
55
|
-
};
|
|
56
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
module.exports = parentDummyChains;
|
|
2
|
-
|
|
3
|
-
function parentDummyChains(g) {
|
|
4
|
-
let postorderNums = postorder(g);
|
|
5
|
-
|
|
6
|
-
g.graph().dummyChains.forEach(v => {
|
|
7
|
-
let node = g.node(v);
|
|
8
|
-
let edgeObj = node.edgeObj;
|
|
9
|
-
let pathData = findPath(g, postorderNums, edgeObj.v, edgeObj.w);
|
|
10
|
-
let path = pathData.path;
|
|
11
|
-
let lca = pathData.lca;
|
|
12
|
-
let pathIdx = 0;
|
|
13
|
-
let pathV = path[pathIdx];
|
|
14
|
-
let ascending = true;
|
|
15
|
-
|
|
16
|
-
while (v !== edgeObj.w) {
|
|
17
|
-
node = g.node(v);
|
|
18
|
-
|
|
19
|
-
if (ascending) {
|
|
20
|
-
while ((pathV = path[pathIdx]) !== lca &&
|
|
21
|
-
g.node(pathV).maxRank < node.rank) {
|
|
22
|
-
pathIdx++;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (pathV === lca) {
|
|
26
|
-
ascending = false;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (!ascending) {
|
|
31
|
-
while (pathIdx < path.length - 1 &&
|
|
32
|
-
g.node(pathV = path[pathIdx + 1]).minRank <= node.rank) {
|
|
33
|
-
pathIdx++;
|
|
34
|
-
}
|
|
35
|
-
pathV = path[pathIdx];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
g.setParent(v, pathV);
|
|
39
|
-
v = g.successors(v)[0];
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Find a path from v to w through the lowest common ancestor (LCA). Return the
|
|
45
|
-
// full path and the LCA.
|
|
46
|
-
function findPath(g, postorderNums, v, w) {
|
|
47
|
-
let vPath = [];
|
|
48
|
-
let wPath = [];
|
|
49
|
-
let low = Math.min(postorderNums[v].low, postorderNums[w].low);
|
|
50
|
-
let lim = Math.max(postorderNums[v].lim, postorderNums[w].lim);
|
|
51
|
-
let parent;
|
|
52
|
-
let lca;
|
|
53
|
-
|
|
54
|
-
// Traverse up from v to find the LCA
|
|
55
|
-
parent = v;
|
|
56
|
-
do {
|
|
57
|
-
parent = g.parent(parent);
|
|
58
|
-
vPath.push(parent);
|
|
59
|
-
} while (parent &&
|
|
60
|
-
(postorderNums[parent].low > low || lim > postorderNums[parent].lim));
|
|
61
|
-
lca = parent;
|
|
62
|
-
|
|
63
|
-
// Traverse from w to LCA
|
|
64
|
-
parent = w;
|
|
65
|
-
while ((parent = g.parent(parent)) !== lca) {
|
|
66
|
-
wPath.push(parent);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return { path: vPath.concat(wPath.reverse()), lca: lca };
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function postorder(g) {
|
|
73
|
-
let result = {};
|
|
74
|
-
let lim = 0;
|
|
75
|
-
|
|
76
|
-
function dfs(v) {
|
|
77
|
-
let low = lim;
|
|
78
|
-
g.children(v).forEach(dfs);
|
|
79
|
-
result[v] = { low: low, lim: lim++ };
|
|
80
|
-
}
|
|
81
|
-
g.children().forEach(dfs);
|
|
82
|
-
|
|
83
|
-
return result;
|
|
84
|
-
}
|