@dagrejs/dagre 1.0.2 → 1.1.0
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/README.md +0 -7
- package/dist/dagre.js +443 -445
- package/dist/dagre.min.js +52 -52
- package/index.d.ts +18 -3
- package/lib/acyclic.js +20 -28
- package/lib/add-border-segments.js +23 -15
- package/lib/coordinate-system.js +13 -23
- package/lib/data/list.js +40 -37
- package/lib/debug.js +31 -22
- package/lib/greedy-fas.js +60 -55
- package/lib/index.js +38 -0
- package/lib/layout.js +158 -155
- package/lib/nesting-graph.js +35 -40
- package/lib/normalize.js +34 -30
- package/lib/order/add-subgraph-constraints.js +9 -5
- package/lib/order/barycenter.js +17 -9
- package/lib/order/build-layer-graph.js +24 -18
- package/lib/order/cross-count.js +22 -19
- package/lib/order/index.js +37 -28
- package/lib/order/init-order.js +14 -13
- package/lib/order/resolve-conflicts.js +20 -30
- package/lib/order/sort-subgraph.js +25 -31
- package/lib/order/sort.js +18 -17
- package/lib/parent-dummy-chains.js +36 -38
- package/lib/position/bk.js +103 -145
- package/lib/position/index.js +14 -13
- package/lib/rank/feasible-tree.js +15 -18
- package/lib/rank/index.js +25 -15
- package/lib/rank/network-simplex.js +22 -43
- package/lib/rank/util.js +6 -12
- package/lib/util.js +69 -84
- package/lib/version.js +8 -1
- package/mjs-lib/acyclic.js +62 -0
- package/mjs-lib/add-border-segments.js +35 -0
- package/mjs-lib/coordinate-system.js +65 -0
- package/mjs-lib/data/list.js +56 -0
- package/mjs-lib/debug.js +30 -0
- package/mjs-lib/greedy-fas.js +125 -0
- package/mjs-lib/index.js +9 -0
- package/mjs-lib/layout.js +405 -0
- package/mjs-lib/nesting-graph.js +120 -0
- package/mjs-lib/normalize.js +84 -0
- package/mjs-lib/order/add-subgraph-constraints.js +49 -0
- package/mjs-lib/order/barycenter.js +24 -0
- package/mjs-lib/order/build-layer-graph.js +71 -0
- package/mjs-lib/order/cross-count.js +64 -0
- package/mjs-lib/order/index.js +70 -0
- package/mjs-lib/order/init-order.js +34 -0
- package/mjs-lib/order/resolve-conflicts.js +116 -0
- package/mjs-lib/order/sort-subgraph.js +71 -0
- package/mjs-lib/order/sort.js +54 -0
- package/mjs-lib/parent-dummy-chains.js +82 -0
- package/mjs-lib/position/bk.js +409 -0
- package/mjs-lib/position/index.js +30 -0
- package/mjs-lib/rank/feasible-tree.js +93 -0
- package/mjs-lib/rank/index.js +46 -0
- package/mjs-lib/rank/network-simplex.js +233 -0
- package/mjs-lib/rank/util.js +58 -0
- package/mjs-lib/util.js +305 -0
- package/mjs-lib/version.js +1 -0
- package/package.json +14 -3
package/mjs-lib/util.js
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/* eslint "no-console": off */
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
import { Graph as Graph } from "@dagrejs/graphlib";
|
|
6
|
+
|
|
7
|
+
export { addBorderNode,
|
|
8
|
+
addDummyNode,
|
|
9
|
+
asNonCompoundGraph,
|
|
10
|
+
buildLayerMatrix,
|
|
11
|
+
intersectRect,
|
|
12
|
+
mapValues,
|
|
13
|
+
maxRank,
|
|
14
|
+
normalizeRanks,
|
|
15
|
+
notime,
|
|
16
|
+
partition,
|
|
17
|
+
pick,
|
|
18
|
+
predecessorWeights,
|
|
19
|
+
range,
|
|
20
|
+
removeEmptyRanks,
|
|
21
|
+
simplify,
|
|
22
|
+
successorWeights,
|
|
23
|
+
time,
|
|
24
|
+
uniqueId,
|
|
25
|
+
zipObject,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/*
|
|
29
|
+
* Adds a dummy node to the graph and return v.
|
|
30
|
+
*/
|
|
31
|
+
function addDummyNode(g, type, attrs, name) {
|
|
32
|
+
let v;
|
|
33
|
+
do {
|
|
34
|
+
v = uniqueId(name);
|
|
35
|
+
} while (g.hasNode(v));
|
|
36
|
+
|
|
37
|
+
attrs.dummy = type;
|
|
38
|
+
g.setNode(v, attrs);
|
|
39
|
+
return v;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/*
|
|
43
|
+
* Returns a new graph with only simple edges. Handles aggregation of data
|
|
44
|
+
* associated with multi-edges.
|
|
45
|
+
*/
|
|
46
|
+
function simplify(g) {
|
|
47
|
+
let simplified = new Graph().setGraph(g.graph());
|
|
48
|
+
g.nodes().forEach(v => simplified.setNode(v, g.node(v)));
|
|
49
|
+
g.edges().forEach(e => {
|
|
50
|
+
let simpleLabel = simplified.edge(e.v, e.w) || { weight: 0, minlen: 1 };
|
|
51
|
+
let label = g.edge(e);
|
|
52
|
+
simplified.setEdge(e.v, e.w, {
|
|
53
|
+
weight: simpleLabel.weight + label.weight,
|
|
54
|
+
minlen: Math.max(simpleLabel.minlen, label.minlen)
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
return simplified;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function asNonCompoundGraph(g) {
|
|
61
|
+
let simplified = new Graph({ multigraph: g.isMultigraph() }).setGraph(g.graph());
|
|
62
|
+
g.nodes().forEach(v => {
|
|
63
|
+
if (!g.children(v).length) {
|
|
64
|
+
simplified.setNode(v, g.node(v));
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
g.edges().forEach(e => {
|
|
68
|
+
simplified.setEdge(e, g.edge(e));
|
|
69
|
+
});
|
|
70
|
+
return simplified;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function successorWeights(g) {
|
|
74
|
+
let weightMap = g.nodes().map(v => {
|
|
75
|
+
let sucs = {};
|
|
76
|
+
g.outEdges(v).forEach(e => {
|
|
77
|
+
sucs[e.w] = (sucs[e.w] || 0) + g.edge(e).weight;
|
|
78
|
+
});
|
|
79
|
+
return sucs;
|
|
80
|
+
});
|
|
81
|
+
return zipObject(g.nodes(), weightMap);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function predecessorWeights(g) {
|
|
85
|
+
let weightMap = g.nodes().map(v => {
|
|
86
|
+
let preds = {};
|
|
87
|
+
g.inEdges(v).forEach(e => {
|
|
88
|
+
preds[e.v] = (preds[e.v] || 0) + g.edge(e).weight;
|
|
89
|
+
});
|
|
90
|
+
return preds;
|
|
91
|
+
});
|
|
92
|
+
return zipObject(g.nodes(), weightMap);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/*
|
|
96
|
+
* Finds where a line starting at point ({x, y}) would intersect a rectangle
|
|
97
|
+
* ({x, y, width, height}) if it were pointing at the rectangle's center.
|
|
98
|
+
*/
|
|
99
|
+
function intersectRect(rect, point) {
|
|
100
|
+
let x = rect.x;
|
|
101
|
+
let y = rect.y;
|
|
102
|
+
|
|
103
|
+
// Rectangle intersection algorithm from:
|
|
104
|
+
// http://math.stackexchange.com/questions/108113/find-edge-between-two-boxes
|
|
105
|
+
let dx = point.x - x;
|
|
106
|
+
let dy = point.y - y;
|
|
107
|
+
let w = rect.width / 2;
|
|
108
|
+
let h = rect.height / 2;
|
|
109
|
+
|
|
110
|
+
if (!dx && !dy) {
|
|
111
|
+
throw new Error("Not possible to find intersection inside of the rectangle");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let sx, sy;
|
|
115
|
+
if (Math.abs(dy) * w > Math.abs(dx) * h) {
|
|
116
|
+
// Intersection is top or bottom of rect.
|
|
117
|
+
if (dy < 0) {
|
|
118
|
+
h = -h;
|
|
119
|
+
}
|
|
120
|
+
sx = h * dx / dy;
|
|
121
|
+
sy = h;
|
|
122
|
+
} else {
|
|
123
|
+
// Intersection is left or right of rect.
|
|
124
|
+
if (dx < 0) {
|
|
125
|
+
w = -w;
|
|
126
|
+
}
|
|
127
|
+
sx = w;
|
|
128
|
+
sy = w * dy / dx;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return { x: x + sx, y: y + sy };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/*
|
|
135
|
+
* Given a DAG with each node assigned "rank" and "order" properties, this
|
|
136
|
+
* function will produce a matrix with the ids of each node.
|
|
137
|
+
*/
|
|
138
|
+
function buildLayerMatrix(g) {
|
|
139
|
+
let layering = range(maxRank(g) + 1).map(() => []);
|
|
140
|
+
g.nodes().forEach(v => {
|
|
141
|
+
let node = g.node(v);
|
|
142
|
+
let rank = node.rank;
|
|
143
|
+
if (rank !== undefined) {
|
|
144
|
+
layering[rank][node.order] = v;
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
return layering;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/*
|
|
151
|
+
* Adjusts the ranks for all nodes in the graph such that all nodes v have
|
|
152
|
+
* rank(v) >= 0 and at least one node w has rank(w) = 0.
|
|
153
|
+
*/
|
|
154
|
+
function normalizeRanks(g) {
|
|
155
|
+
let min = Math.min(...g.nodes().map(v => {
|
|
156
|
+
let rank = g.node(v).rank;
|
|
157
|
+
if (rank === undefined) {
|
|
158
|
+
return Number.MAX_VALUE;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return rank;
|
|
162
|
+
}));
|
|
163
|
+
g.nodes().forEach(v => {
|
|
164
|
+
let node = g.node(v);
|
|
165
|
+
if (node.hasOwnProperty("rank")) {
|
|
166
|
+
node.rank -= min;
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function removeEmptyRanks(g) {
|
|
172
|
+
// Ranks may not start at 0, so we need to offset them
|
|
173
|
+
let offset = Math.min(...g.nodes().map(v => g.node(v).rank));
|
|
174
|
+
|
|
175
|
+
let layers = [];
|
|
176
|
+
g.nodes().forEach(v => {
|
|
177
|
+
let rank = g.node(v).rank - offset;
|
|
178
|
+
if (!layers[rank]) {
|
|
179
|
+
layers[rank] = [];
|
|
180
|
+
}
|
|
181
|
+
layers[rank].push(v);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
let delta = 0;
|
|
185
|
+
let nodeRankFactor = g.graph().nodeRankFactor;
|
|
186
|
+
Array.from(layers).forEach((vs, i) => {
|
|
187
|
+
if (vs === undefined && i % nodeRankFactor !== 0) {
|
|
188
|
+
--delta;
|
|
189
|
+
} else if (vs !== undefined && delta) {
|
|
190
|
+
vs.forEach(v => g.node(v).rank += delta);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function addBorderNode(g, prefix, rank, order) {
|
|
196
|
+
let node = {
|
|
197
|
+
width: 0,
|
|
198
|
+
height: 0
|
|
199
|
+
};
|
|
200
|
+
if (arguments.length >= 4) {
|
|
201
|
+
node.rank = rank;
|
|
202
|
+
node.order = order;
|
|
203
|
+
}
|
|
204
|
+
return addDummyNode(g, "border", node, prefix);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function maxRank(g) {
|
|
208
|
+
return Math.max(...g.nodes().map(v => {
|
|
209
|
+
let rank = g.node(v).rank;
|
|
210
|
+
if (rank === undefined) {
|
|
211
|
+
return Number.MIN_VALUE;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return rank;
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/*
|
|
219
|
+
* Partition a collection into two groups: `lhs` and `rhs`. If the supplied
|
|
220
|
+
* function returns true for an entry it goes into `lhs`. Otherwise it goes
|
|
221
|
+
* into `rhs.
|
|
222
|
+
*/
|
|
223
|
+
function partition(collection, fn) {
|
|
224
|
+
let result = { lhs: [], rhs: [] };
|
|
225
|
+
collection.forEach(value => {
|
|
226
|
+
if (fn(value)) {
|
|
227
|
+
result.lhs.push(value);
|
|
228
|
+
} else {
|
|
229
|
+
result.rhs.push(value);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
return result;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/*
|
|
236
|
+
* Returns a new function that wraps `fn` with a timer. The wrapper logs the
|
|
237
|
+
* time it takes to execute the function.
|
|
238
|
+
*/
|
|
239
|
+
function time(name, fn) {
|
|
240
|
+
let start = Date.now();
|
|
241
|
+
try {
|
|
242
|
+
return fn();
|
|
243
|
+
} finally {
|
|
244
|
+
console.log(name + " time: " + (Date.now() - start) + "ms");
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function notime(name, fn) {
|
|
249
|
+
return fn();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
let idCounter = 0;
|
|
253
|
+
function uniqueId(prefix) {
|
|
254
|
+
var id = ++idCounter;
|
|
255
|
+
return toString(prefix) + id;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function range(start, limit, step = 1) {
|
|
259
|
+
if (limit == null) {
|
|
260
|
+
limit = start;
|
|
261
|
+
start = 0;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
let endCon = (i) => i < limit;
|
|
265
|
+
if (step < 0) {
|
|
266
|
+
endCon = (i) => limit < i;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const range = [];
|
|
270
|
+
for (let i = start; endCon(i); i += step) {
|
|
271
|
+
range.push(i);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return range;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function pick(source, keys) {
|
|
278
|
+
const dest = {};
|
|
279
|
+
for (const key of keys) {
|
|
280
|
+
if (source[key] !== undefined) {
|
|
281
|
+
dest[key] = source[key];
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return dest;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function mapValues(obj, funcOrProp) {
|
|
289
|
+
let func = funcOrProp;
|
|
290
|
+
if (typeof funcOrProp === 'string') {
|
|
291
|
+
func = (val) => val[funcOrProp];
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return Object.entries(obj).reduce((acc, [k, v]) => {
|
|
295
|
+
acc[k] = func(v, k);
|
|
296
|
+
return acc;
|
|
297
|
+
}, {});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function zipObject(props, values) {
|
|
301
|
+
return props.reduce((acc, key, i) => {
|
|
302
|
+
acc[key] = values[i];
|
|
303
|
+
return acc;
|
|
304
|
+
}, {});
|
|
305
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default "1.1.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dagrejs/dagre",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Graph layout for JavaScript",
|
|
5
5
|
"author": "Chris Pettitt <cpettitt@gmail.com>",
|
|
6
6
|
"contributors": [
|
|
@@ -12,11 +12,17 @@
|
|
|
12
12
|
"lint": "make lint",
|
|
13
13
|
"test": "make test"
|
|
14
14
|
},
|
|
15
|
+
"module": "./mjs-lib/index.js",
|
|
16
|
+
"exports": {
|
|
17
|
+
"import": "./mjs-lib/index.js",
|
|
18
|
+
"require": "./index.js"
|
|
19
|
+
},
|
|
15
20
|
"files": [
|
|
16
21
|
"index.js",
|
|
17
22
|
"index.d.ts",
|
|
18
23
|
"dist/",
|
|
19
|
-
"lib/"
|
|
24
|
+
"lib/",
|
|
25
|
+
"mjs-lib/"
|
|
20
26
|
],
|
|
21
27
|
"types": "index.d.ts",
|
|
22
28
|
"keywords": [
|
|
@@ -24,9 +30,14 @@
|
|
|
24
30
|
"layout"
|
|
25
31
|
],
|
|
26
32
|
"dependencies": {
|
|
27
|
-
"@dagrejs/graphlib": "2.
|
|
33
|
+
"@dagrejs/graphlib": "2.2.0"
|
|
28
34
|
},
|
|
29
35
|
"devDependencies": {
|
|
36
|
+
"@babel/cli": "^7.23.9",
|
|
37
|
+
"@babel/core": "^7.23.9",
|
|
38
|
+
"@babel/plugin-transform-export-namespace-from": "^7.23.4",
|
|
39
|
+
"@babel/plugin-transform-modules-commonjs": "^7.23.3",
|
|
40
|
+
"babel-plugin-add-module-exports": "^1.0.4",
|
|
30
41
|
"benchmark": "2.1.4",
|
|
31
42
|
"browserify": "17.0.0",
|
|
32
43
|
"chai": "4.3.6",
|