@flowgram-vue/free-auto-layout-plugin 0.2.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.
Files changed (58) hide show
  1. package/LICENSE +22 -0
  2. package/dist/index.cjs +2713 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.ts +377 -0
  5. package/dist/index.js +2705 -0
  6. package/dist/index.js.map +1 -0
  7. package/package.json +57 -0
  8. package/src/create-auto-layout-plugin.ts +23 -0
  9. package/src/dagre-layout/acyclic.ts +129 -0
  10. package/src/dagre-layout/graph.ts +71 -0
  11. package/src/dagre-layout/index.ts +9 -0
  12. package/src/dagre-layout/layout.ts +151 -0
  13. package/src/dagre-layout/order.ts +261 -0
  14. package/src/dagre-layout/rank/feasible-tree.ts +102 -0
  15. package/src/dagre-layout/rank/index.ts +9 -0
  16. package/src/dagre-layout/rank/longest-path.ts +79 -0
  17. package/src/dagre-layout/rank/network-simplex.ts +235 -0
  18. package/src/dagre-layout/rank/normalize-ranks.ts +26 -0
  19. package/src/dagre-layout/type.ts +43 -0
  20. package/src/dagre-lib/acyclic.js +72 -0
  21. package/src/dagre-lib/add-border-segments.js +41 -0
  22. package/src/dagre-lib/coordinate-system.js +77 -0
  23. package/src/dagre-lib/data/list.js +63 -0
  24. package/src/dagre-lib/debug.js +34 -0
  25. package/src/dagre-lib/greedy-fas.js +134 -0
  26. package/src/dagre-lib/index.js +75 -0
  27. package/src/dagre-lib/layout.js +449 -0
  28. package/src/dagre-lib/nesting-graph.js +133 -0
  29. package/src/dagre-lib/normalize.js +98 -0
  30. package/src/dagre-lib/order/add-subgraph-constraints.js +57 -0
  31. package/src/dagre-lib/order/barycenter.js +34 -0
  32. package/src/dagre-lib/order/build-layer-graph.js +80 -0
  33. package/src/dagre-lib/order/cross-count.js +78 -0
  34. package/src/dagre-lib/order/index.js +86 -0
  35. package/src/dagre-lib/order/init-order.js +43 -0
  36. package/src/dagre-lib/order/resolve-conflicts.js +128 -0
  37. package/src/dagre-lib/order/sort-subgraph.js +79 -0
  38. package/src/dagre-lib/order/sort.js +62 -0
  39. package/src/dagre-lib/parent-dummy-chains.js +90 -0
  40. package/src/dagre-lib/position/bk.js +431 -0
  41. package/src/dagre-lib/position/index.js +37 -0
  42. package/src/dagre-lib/rank/feasible-tree.js +104 -0
  43. package/src/dagre-lib/rank/index.js +61 -0
  44. package/src/dagre-lib/rank/network-simplex.js +243 -0
  45. package/src/dagre-lib/rank/util.js +70 -0
  46. package/src/dagre-lib/util.js +365 -0
  47. package/src/dagre-lib/version.js +6 -0
  48. package/src/env.d.ts +10 -0
  49. package/src/index.ts +10 -0
  50. package/src/layout/constant.ts +26 -0
  51. package/src/layout/dagre.ts +245 -0
  52. package/src/layout/index.ts +9 -0
  53. package/src/layout/layout.ts +41 -0
  54. package/src/layout/position.ts +72 -0
  55. package/src/layout/store.ts +262 -0
  56. package/src/layout/type.ts +162 -0
  57. package/src/services.ts +174 -0
  58. package/src/type.ts +10 -0
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ import rankUtil from './util';
9
+ import { longestPath } from './util';
10
+ import feasibleTree from './feasible-tree';
11
+ import networkSimplex from './network-simplex';
12
+
13
+ export { rank };
14
+ export default rank;
15
+
16
+ /*
17
+ * Assigns a rank to each node in the input graph that respects the "minlen"
18
+ * constraint specified on edges between nodes.
19
+ *
20
+ * This basic structure is derived from Gansner, et al., "A Technique for
21
+ * Drawing Directed Graphs."
22
+ *
23
+ * Pre-conditions:
24
+ *
25
+ * 1. Graph must be a connected DAG
26
+ * 2. Graph nodes must be objects
27
+ * 3. Graph edges must have "weight" and "minlen" attributes
28
+ *
29
+ * Post-conditions:
30
+ *
31
+ * 1. Graph nodes will have a "rank" attribute based on the results of the
32
+ * algorithm. Ranks can start at any index (including negative), we'll
33
+ * fix them up later.
34
+ */
35
+ function rank(g) {
36
+ switch (g.graph().ranker) {
37
+ case 'network-simplex':
38
+ networkSimplexRanker(g);
39
+ break;
40
+ case 'tight-tree':
41
+ tightTreeRanker(g);
42
+ break;
43
+ case 'longest-path':
44
+ longestPathRanker(g);
45
+ break;
46
+ default:
47
+ networkSimplexRanker(g);
48
+ }
49
+ }
50
+
51
+ // A fast and simple ranker, but results are far from optimal.
52
+ var longestPathRanker = longestPath;
53
+
54
+ function tightTreeRanker(g) {
55
+ longestPath(g);
56
+ feasibleTree(g);
57
+ }
58
+
59
+ function networkSimplexRanker(g) {
60
+ networkSimplex(g);
61
+ }
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ import { alg } from '@dagrejs/graphlib';
9
+ import { feasibleTree } from './feasible-tree';
10
+ import { slack, longestPath as initRank } from './util';
11
+ import { simplify } from '../util';
12
+
13
+ const { preorder, postorder } = alg;
14
+
15
+ export { networkSimplex };
16
+ export default networkSimplex;
17
+
18
+ // Expose some internals for testing purposes
19
+ networkSimplex.initLowLimValues = initLowLimValues;
20
+ networkSimplex.initCutValues = initCutValues;
21
+ networkSimplex.calcCutValue = calcCutValue;
22
+ networkSimplex.leaveEdge = leaveEdge;
23
+ networkSimplex.enterEdge = enterEdge;
24
+ networkSimplex.exchangeEdges = exchangeEdges;
25
+
26
+ /*
27
+ * The network simplex algorithm assigns ranks to each node in the input graph
28
+ * and iteratively improves the ranking to reduce the length of edges.
29
+ *
30
+ * Preconditions:
31
+ *
32
+ * 1. The input graph must be a DAG.
33
+ * 2. All nodes in the graph must have an object value.
34
+ * 3. All edges in the graph must have "minlen" and "weight" attributes.
35
+ *
36
+ * Postconditions:
37
+ *
38
+ * 1. All nodes in the graph will have an assigned "rank" attribute that has
39
+ * been optimized by the network simplex algorithm. Ranks start at 0.
40
+ *
41
+ *
42
+ * A rough sketch of the algorithm is as follows:
43
+ *
44
+ * 1. Assign initial ranks to each node. We use the longest path algorithm,
45
+ * which assigns ranks to the lowest position possible. In general this
46
+ * leads to very wide bottom ranks and unnecessarily long edges.
47
+ * 2. Construct a feasible tight tree. A tight tree is one such that all
48
+ * edges in the tree have no slack (difference between length of edge
49
+ * and minlen for the edge). This by itself greatly improves the assigned
50
+ * rankings by shorting edges.
51
+ * 3. Iteratively find edges that have negative cut values. Generally a
52
+ * negative cut value indicates that the edge could be removed and a new
53
+ * tree edge could be added to produce a more compact graph.
54
+ *
55
+ * Much of the algorithms here are derived from Gansner, et al., "A Technique
56
+ * for Drawing Directed Graphs." The structure of the file roughly follows the
57
+ * structure of the overall algorithm.
58
+ */
59
+ function networkSimplex(g) {
60
+ g = simplify(g);
61
+ initRank(g);
62
+ var t = feasibleTree(g);
63
+ initLowLimValues(t);
64
+ initCutValues(t, g);
65
+
66
+ var e, f;
67
+ while ((e = leaveEdge(t))) {
68
+ f = enterEdge(t, g, e);
69
+ exchangeEdges(t, g, e, f);
70
+ }
71
+ }
72
+
73
+ /*
74
+ * Initializes cut values for all edges in the tree.
75
+ */
76
+ function initCutValues(t, g) {
77
+ var vs = postorder(t, t.nodes());
78
+ vs = vs.slice(0, vs.length - 1);
79
+ vs.forEach((v) => assignCutValue(t, g, v));
80
+ }
81
+
82
+ function assignCutValue(t, g, child) {
83
+ var childLab = t.node(child);
84
+ var parent = childLab.parent;
85
+ t.edge(child, parent).cutvalue = calcCutValue(t, g, child);
86
+ }
87
+
88
+ /*
89
+ * Given the tight tree, its graph, and a child in the graph calculate and
90
+ * return the cut value for the edge between the child and its parent.
91
+ */
92
+ function calcCutValue(t, g, child) {
93
+ var childLab = t.node(child);
94
+ var parent = childLab.parent;
95
+ // True if the child is on the tail end of the edge in the directed graph
96
+ var childIsTail = true;
97
+ // The graph's view of the tree edge we're inspecting
98
+ var graphEdge = g.edge(child, parent);
99
+ // The accumulated cut value for the edge between this node and its parent
100
+ var cutValue = 0;
101
+
102
+ if (!graphEdge) {
103
+ childIsTail = false;
104
+ graphEdge = g.edge(parent, child);
105
+ }
106
+
107
+ cutValue = graphEdge.weight;
108
+
109
+ g.nodeEdges(child).forEach((e) => {
110
+ var isOutEdge = e.v === child,
111
+ other = isOutEdge ? e.w : e.v;
112
+
113
+ if (other !== parent) {
114
+ var pointsToHead = isOutEdge === childIsTail,
115
+ otherWeight = g.edge(e).weight;
116
+
117
+ cutValue += pointsToHead ? otherWeight : -otherWeight;
118
+ if (isTreeEdge(t, child, other)) {
119
+ var otherCutValue = t.edge(child, other).cutvalue;
120
+ cutValue += pointsToHead ? -otherCutValue : otherCutValue;
121
+ }
122
+ }
123
+ });
124
+
125
+ return cutValue;
126
+ }
127
+
128
+ function initLowLimValues(tree, root) {
129
+ if (arguments.length < 2) {
130
+ root = tree.nodes()[0];
131
+ }
132
+ dfsAssignLowLim(tree, {}, 1, root);
133
+ }
134
+
135
+ function dfsAssignLowLim(tree, visited, nextLim, v, parent) {
136
+ var low = nextLim;
137
+ var label = tree.node(v);
138
+
139
+ visited[v] = true;
140
+ tree.neighbors(v).forEach((w) => {
141
+ if (!Object.hasOwn(visited, w)) {
142
+ nextLim = dfsAssignLowLim(tree, visited, nextLim, w, v);
143
+ }
144
+ });
145
+
146
+ label.low = low;
147
+ label.lim = nextLim++;
148
+ if (parent) {
149
+ label.parent = parent;
150
+ } else {
151
+ // TODO should be able to remove this when we incrementally update low lim
152
+ delete label.parent;
153
+ }
154
+
155
+ return nextLim;
156
+ }
157
+
158
+ function leaveEdge(tree) {
159
+ return tree.edges().find((e) => tree.edge(e).cutvalue < 0);
160
+ }
161
+
162
+ function enterEdge(t, g, edge) {
163
+ var v = edge.v;
164
+ var w = edge.w;
165
+
166
+ // For the rest of this function we assume that v is the tail and w is the
167
+ // head, so if we don't have this edge in the graph we should flip it to
168
+ // match the correct orientation.
169
+ if (!g.hasEdge(v, w)) {
170
+ v = edge.w;
171
+ w = edge.v;
172
+ }
173
+
174
+ var vLabel = t.node(v);
175
+ var wLabel = t.node(w);
176
+ var tailLabel = vLabel;
177
+ var flip = false;
178
+
179
+ // If the root is in the tail of the edge then we need to flip the logic that
180
+ // checks for the head and tail nodes in the candidates function below.
181
+ if (vLabel.lim > wLabel.lim) {
182
+ tailLabel = wLabel;
183
+ flip = true;
184
+ }
185
+
186
+ var candidates = g.edges().filter((edge) => {
187
+ return (
188
+ flip === isDescendant(t, t.node(edge.v), tailLabel) &&
189
+ flip !== isDescendant(t, t.node(edge.w), tailLabel)
190
+ );
191
+ });
192
+
193
+ return candidates.reduce((acc, edge) => {
194
+ if (slack(g, edge) < slack(g, acc)) {
195
+ return edge;
196
+ }
197
+
198
+ return acc;
199
+ });
200
+ }
201
+
202
+ function exchangeEdges(t, g, e, f) {
203
+ var v = e.v;
204
+ var w = e.w;
205
+ t.removeEdge(v, w);
206
+ t.setEdge(f.v, f.w, {});
207
+ initLowLimValues(t);
208
+ initCutValues(t, g);
209
+ updateRanks(t, g);
210
+ }
211
+
212
+ function updateRanks(t, g) {
213
+ var root = t.nodes().find((v) => !g.node(v).parent);
214
+ var vs = preorder(t, root);
215
+ vs = vs.slice(1);
216
+ vs.forEach((v) => {
217
+ var parent = t.node(v).parent,
218
+ edge = g.edge(v, parent),
219
+ flipped = false;
220
+
221
+ if (!edge) {
222
+ edge = g.edge(parent, v);
223
+ flipped = true;
224
+ }
225
+
226
+ g.node(v).rank = g.node(parent).rank + (flipped ? edge.minlen : -edge.minlen);
227
+ });
228
+ }
229
+
230
+ /*
231
+ * Returns true if the edge is in the tree.
232
+ */
233
+ function isTreeEdge(tree, u, v) {
234
+ return tree.hasEdge(u, v);
235
+ }
236
+
237
+ /*
238
+ * Returns true if the specified node is descendant of the root node per the
239
+ * assigned low and lim attributes in the tree.
240
+ */
241
+ function isDescendant(tree, vLabel, rootLabel) {
242
+ return rootLabel.low <= vLabel.lim && vLabel.lim <= rootLabel.lim;
243
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ import { applyWithChunking } from '../util';
9
+
10
+ export { longestPath, slack };
11
+ export default { longestPath, slack };
12
+
13
+ /*
14
+ * Initializes ranks for the input graph using the longest path algorithm. This
15
+ * algorithm scales well and is fast in practice, it yields rather poor
16
+ * solutions. Nodes are pushed to the lowest layer possible, leaving the bottom
17
+ * ranks wide and leaving edges longer than necessary. However, due to its
18
+ * speed, this algorithm is good for getting an initial ranking that can be fed
19
+ * into other algorithms.
20
+ *
21
+ * This algorithm does not normalize layers because it will be used by other
22
+ * algorithms in most cases. If using this algorithm directly, be sure to
23
+ * run normalize at the end.
24
+ *
25
+ * Pre-conditions:
26
+ *
27
+ * 1. Input graph is a DAG.
28
+ * 2. Input graph node labels can be assigned properties.
29
+ *
30
+ * Post-conditions:
31
+ *
32
+ * 1. Each node will be assign an (unnormalized) "rank" property.
33
+ */
34
+ function longestPath(g) {
35
+ var visited = {};
36
+
37
+ function dfs(v) {
38
+ var label = g.node(v);
39
+ if (Object.hasOwn(visited, v)) {
40
+ return label.rank;
41
+ }
42
+ visited[v] = true;
43
+
44
+ let outEdgesMinLens = g.outEdges(v).map((e) => {
45
+ if (e == null) {
46
+ return Number.POSITIVE_INFINITY;
47
+ }
48
+
49
+ return dfs(e.w) - g.edge(e).minlen;
50
+ });
51
+
52
+ var rank = applyWithChunking(Math.min, outEdgesMinLens);
53
+
54
+ if (rank === Number.POSITIVE_INFINITY) {
55
+ rank = 0;
56
+ }
57
+
58
+ return (label.rank = rank);
59
+ }
60
+
61
+ g.sources().forEach(dfs);
62
+ }
63
+
64
+ /*
65
+ * Returns the amount of slack for the given edge. The slack is defined as the
66
+ * difference between the length of the edge and its minimum length.
67
+ */
68
+ function slack(g, e) {
69
+ return g.node(e.w).rank - g.node(e.v).rank - g.edge(e).minlen;
70
+ }