@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,57 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ export { addSubgraphConstraints };
7
+ export default addSubgraphConstraints;
8
+
9
+ function addSubgraphConstraints(g, cg, vs) {
10
+ let prev = {},
11
+ rootPrev;
12
+
13
+ vs.forEach((v) => {
14
+ let child = g.parent(v),
15
+ parent,
16
+ prevChild;
17
+ while (child) {
18
+ parent = g.parent(child);
19
+ if (parent) {
20
+ prevChild = prev[parent];
21
+ prev[parent] = child;
22
+ } else {
23
+ prevChild = rootPrev;
24
+ rootPrev = child;
25
+ }
26
+ if (prevChild && prevChild !== child) {
27
+ cg.setEdge(prevChild, child);
28
+ return;
29
+ }
30
+ child = parent;
31
+ }
32
+ });
33
+
34
+ /*
35
+ function dfs(v) {
36
+ var children = v ? g.children(v) : g.children();
37
+ if (children.length) {
38
+ var min = Number.POSITIVE_INFINITY,
39
+ subgraphs = [];
40
+ children.forEach(function(child) {
41
+ var childMin = dfs(child);
42
+ if (g.children(child).length) {
43
+ subgraphs.push({ v: child, order: childMin });
44
+ }
45
+ min = Math.min(min, childMin);
46
+ });
47
+ _.sortBy(subgraphs, "order").reduce(function(prev, curr) {
48
+ cg.setEdge(prev.v, curr.v);
49
+ return curr;
50
+ });
51
+ return min;
52
+ }
53
+ return g.node(v).order;
54
+ }
55
+ dfs(undefined);
56
+ */
57
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ export { barycenter };
7
+ export default barycenter;
8
+
9
+ function barycenter(g, movable = []) {
10
+ return movable.map((v) => {
11
+ let inV = g.inEdges(v);
12
+ if (!inV.length) {
13
+ return { v: v };
14
+ } else {
15
+ let result = inV.reduce(
16
+ (acc, e) => {
17
+ let edge = g.edge(e),
18
+ nodeU = g.node(e.v);
19
+ return {
20
+ sum: acc.sum + edge.weight * nodeU.order,
21
+ weight: acc.weight + edge.weight,
22
+ };
23
+ },
24
+ { sum: 0, weight: 0 }
25
+ );
26
+
27
+ return {
28
+ v: v,
29
+ barycenter: result.sum / result.weight,
30
+ weight: result.weight,
31
+ };
32
+ }
33
+ });
34
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import { Graph } from '@dagrejs/graphlib';
7
+ import { util } from '../util';
8
+
9
+ export { buildLayerGraph };
10
+ export default buildLayerGraph;
11
+
12
+ /*
13
+ * Constructs a graph that can be used to sort a layer of nodes. The graph will
14
+ * contain all base and subgraph nodes from the request layer in their original
15
+ * hierarchy and any edges that are incident on these nodes and are of the type
16
+ * requested by the "relationship" parameter.
17
+ *
18
+ * Nodes from the requested rank that do not have parents are assigned a root
19
+ * node in the output graph, which is set in the root graph attribute. This
20
+ * makes it easy to walk the hierarchy of movable nodes during ordering.
21
+ *
22
+ * Pre-conditions:
23
+ *
24
+ * 1. Input graph is a DAG
25
+ * 2. Base nodes in the input graph have a rank attribute
26
+ * 3. Subgraph nodes in the input graph has minRank and maxRank attributes
27
+ * 4. Edges have an assigned weight
28
+ *
29
+ * Post-conditions:
30
+ *
31
+ * 1. Output graph has all nodes in the movable rank with preserved
32
+ * hierarchy.
33
+ * 2. Root nodes in the movable layer are made children of the node
34
+ * indicated by the root attribute of the graph.
35
+ * 3. Non-movable nodes incident on movable nodes, selected by the
36
+ * relationship parameter, are included in the graph (without hierarchy).
37
+ * 4. Edges incident on movable nodes, selected by the relationship
38
+ * parameter, are added to the output graph.
39
+ * 5. The weights for copied edges are aggregated as need, since the output
40
+ * graph is not a multi-graph.
41
+ */
42
+ function buildLayerGraph(g, rank, relationship) {
43
+ let root = createRootNode(g),
44
+ result = new Graph({ compound: true })
45
+ .setGraph({ root: root })
46
+ .setDefaultNodeLabel((v) => g.node(v));
47
+
48
+ g.nodes().forEach((v) => {
49
+ let node = g.node(v),
50
+ parent = g.parent(v);
51
+
52
+ if (node.rank === rank || (node.minRank <= rank && rank <= node.maxRank)) {
53
+ result.setNode(v);
54
+ result.setParent(v, parent || root);
55
+
56
+ // This assumes we have only short edges!
57
+ g[relationship](v).forEach((e) => {
58
+ let u = e.v === v ? e.w : e.v,
59
+ edge = result.edge(u, v),
60
+ weight = edge !== undefined ? edge.weight : 0;
61
+ result.setEdge(u, v, { weight: g.edge(e).weight + weight });
62
+ });
63
+
64
+ if (Object.hasOwn(node, 'minRank')) {
65
+ result.setNode(v, {
66
+ borderLeft: node.borderLeft[rank],
67
+ borderRight: node.borderRight[rank],
68
+ });
69
+ }
70
+ }
71
+ });
72
+
73
+ return result;
74
+ }
75
+
76
+ function createRootNode(g) {
77
+ var v;
78
+ while (g.hasNode((v = util.uniqueId('_root'))));
79
+ return v;
80
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ import { zipObject } from '../util';
9
+
10
+ export { crossCount };
11
+ export default crossCount;
12
+
13
+ /*
14
+ * A function that takes a layering (an array of layers, each with an array of
15
+ * ordererd nodes) and a graph and returns a weighted crossing count.
16
+ *
17
+ * Pre-conditions:
18
+ *
19
+ * 1. Input graph must be simple (not a multigraph), directed, and include
20
+ * only simple edges.
21
+ * 2. Edges in the input graph must have assigned weights.
22
+ *
23
+ * Post-conditions:
24
+ *
25
+ * 1. The graph and layering matrix are left unchanged.
26
+ *
27
+ * This algorithm is derived from Barth, et al., "Bilayer Cross Counting."
28
+ */
29
+ function crossCount(g, layering) {
30
+ let cc = 0;
31
+ for (let i = 1; i < layering.length; ++i) {
32
+ cc += twoLayerCrossCount(g, layering[i - 1], layering[i]);
33
+ }
34
+ return cc;
35
+ }
36
+
37
+ function twoLayerCrossCount(g, northLayer, southLayer) {
38
+ // Sort all of the edges between the north and south layers by their position
39
+ // in the north layer and then the south. Map these edges to the position of
40
+ // their head in the south layer.
41
+ let southPos = zipObject(
42
+ southLayer,
43
+ southLayer.map((v, i) => i)
44
+ );
45
+ let southEntries = northLayer.flatMap((v) => {
46
+ return g
47
+ .outEdges(v)
48
+ .map((e) => {
49
+ return { pos: southPos[e.w], weight: g.edge(e).weight };
50
+ })
51
+ .sort((a, b) => a.pos - b.pos);
52
+ });
53
+
54
+ // Build the accumulator tree
55
+ let firstIndex = 1;
56
+ while (firstIndex < southLayer.length) firstIndex <<= 1;
57
+ let treeSize = 2 * firstIndex - 1;
58
+ firstIndex -= 1;
59
+ let tree = new Array(treeSize).fill(0);
60
+
61
+ // Calculate the weighted crossings
62
+ let cc = 0;
63
+ southEntries.forEach((entry) => {
64
+ let index = entry.pos + firstIndex;
65
+ tree[index] += entry.weight;
66
+ let weightSum = 0;
67
+ while (index > 0) {
68
+ if (index % 2) {
69
+ weightSum += tree[index + 1];
70
+ }
71
+ index = (index - 1) >> 1;
72
+ tree[index] += entry.weight;
73
+ }
74
+ cc += entry.weight * weightSum;
75
+ });
76
+
77
+ return cc;
78
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ import initOrder from './init-order';
9
+ import crossCount from './cross-count';
10
+ import sortSubgraph from './sort-subgraph';
11
+ import buildLayerGraph from './build-layer-graph';
12
+ import addSubgraphConstraints from './add-subgraph-constraints';
13
+ import { Graph } from '@dagrejs/graphlib';
14
+ import { util } from '../util';
15
+
16
+ export default order;
17
+
18
+ /*
19
+ * Applies heuristics to minimize edge crossings in the graph and sets the best
20
+ * order solution as an order attribute on each node.
21
+ *
22
+ * Pre-conditions:
23
+ *
24
+ * 1. Graph must be DAG
25
+ * 2. Graph nodes must be objects with a "rank" attribute
26
+ * 3. Graph edges must have the "weight" attribute
27
+ *
28
+ * Post-conditions:
29
+ *
30
+ * 1. Graph nodes will have an "order" attribute based on the results of the
31
+ * algorithm.
32
+ */
33
+ function order(g, opts) {
34
+ if (opts && typeof opts.customOrder === 'function') {
35
+ opts.customOrder(g, order);
36
+ return;
37
+ }
38
+
39
+ let maxRank = util.maxRank(g),
40
+ downLayerGraphs = buildLayerGraphs(g, util.range(1, maxRank + 1), 'inEdges'),
41
+ upLayerGraphs = buildLayerGraphs(g, util.range(maxRank - 1, -1, -1), 'outEdges');
42
+
43
+ let layering = initOrder(g);
44
+ assignOrder(g, layering);
45
+
46
+ if (opts && opts.disableOptimalOrderHeuristic) {
47
+ return;
48
+ }
49
+
50
+ let bestCC = Number.POSITIVE_INFINITY,
51
+ best;
52
+
53
+ for (let i = 0, lastBest = 0; lastBest < 4; ++i, ++lastBest) {
54
+ sweepLayerGraphs(i % 2 ? downLayerGraphs : upLayerGraphs, i % 4 >= 2);
55
+
56
+ layering = util.buildLayerMatrix(g);
57
+ let cc = crossCount(g, layering);
58
+ if (cc < bestCC) {
59
+ lastBest = 0;
60
+ best = Object.assign({}, layering);
61
+ bestCC = cc;
62
+ }
63
+ }
64
+
65
+ assignOrder(g, best);
66
+ }
67
+
68
+ function buildLayerGraphs(g, ranks, relationship) {
69
+ return ranks.map(function (rank) {
70
+ return buildLayerGraph(g, rank, relationship);
71
+ });
72
+ }
73
+
74
+ function sweepLayerGraphs(layerGraphs, biasRight) {
75
+ let cg = new Graph();
76
+ layerGraphs.forEach(function (lg) {
77
+ let root = lg.graph().root;
78
+ let sorted = sortSubgraph(lg, root, cg, biasRight);
79
+ sorted.vs.forEach((v, i) => (lg.node(v).order = i));
80
+ addSubgraphConstraints(lg, cg, sorted.vs);
81
+ });
82
+ }
83
+
84
+ function assignOrder(g, layering) {
85
+ Object.values(layering).forEach((layer) => layer.forEach((v, i) => (g.node(v).order = i)));
86
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ import { util } from '../util';
9
+
10
+ export { initOrder };
11
+ export default initOrder;
12
+
13
+ /*
14
+ * Assigns an initial order value for each node by performing a DFS search
15
+ * starting from nodes in the first rank. Nodes are assigned an order in their
16
+ * rank as they are first visited.
17
+ *
18
+ * This approach comes from Gansner, et al., "A Technique for Drawing Directed
19
+ * Graphs."
20
+ *
21
+ * Returns a layering matrix with an array per layer and each layer sorted by
22
+ * the order of its nodes.
23
+ */
24
+ function initOrder(g) {
25
+ let visited = {};
26
+ let simpleNodes = g.nodes().filter((v) => !g.children(v).length);
27
+ let simpleNodesRanks = simpleNodes.map((v) => g.node(v).rank);
28
+ let maxRank = util.applyWithChunking(Math.max, simpleNodesRanks);
29
+ let layers = util.range(maxRank + 1).map(() => []);
30
+
31
+ function dfs(v) {
32
+ if (visited[v]) return;
33
+ visited[v] = true;
34
+ let node = g.node(v);
35
+ layers[node.rank].push(v);
36
+ g.successors(v).forEach(dfs);
37
+ }
38
+
39
+ let orderedVs = simpleNodes.sort((a, b) => g.node(a).rank - g.node(b).rank);
40
+ orderedVs.forEach(dfs);
41
+
42
+ return layers;
43
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ import util from '../util';
9
+
10
+ export { resolveConflicts };
11
+ export default resolveConflicts;
12
+
13
+ /*
14
+ * Given a list of entries of the form {v, barycenter, weight} and a
15
+ * constraint graph this function will resolve any conflicts between the
16
+ * constraint graph and the barycenters for the entries. If the barycenters for
17
+ * an entry would violate a constraint in the constraint graph then we coalesce
18
+ * the nodes in the conflict into a new node that respects the contraint and
19
+ * aggregates barycenter and weight information.
20
+ *
21
+ * This implementation is based on the description in Forster, "A Fast and
22
+ * Simple Hueristic for Constrained Two-Level Crossing Reduction," thought it
23
+ * differs in some specific details.
24
+ *
25
+ * Pre-conditions:
26
+ *
27
+ * 1. Each entry has the form {v, barycenter, weight}, or if the node has
28
+ * no barycenter, then {v}.
29
+ *
30
+ * Returns:
31
+ *
32
+ * A new list of entries of the form {vs, i, barycenter, weight}. The list
33
+ * `vs` may either be a singleton or it may be an aggregation of nodes
34
+ * ordered such that they do not violate constraints from the constraint
35
+ * graph. The property `i` is the lowest original index of any of the
36
+ * elements in `vs`.
37
+ */
38
+ function resolveConflicts(entries, cg) {
39
+ let mappedEntries = {};
40
+ entries.forEach((entry, i) => {
41
+ let tmp = (mappedEntries[entry.v] = {
42
+ indegree: 0,
43
+ in: [],
44
+ out: [],
45
+ vs: [entry.v],
46
+ i: i,
47
+ });
48
+ if (entry.barycenter !== undefined) {
49
+ tmp.barycenter = entry.barycenter;
50
+ tmp.weight = entry.weight;
51
+ }
52
+ });
53
+
54
+ cg.edges().forEach((e) => {
55
+ let entryV = mappedEntries[e.v];
56
+ let entryW = mappedEntries[e.w];
57
+ if (entryV !== undefined && entryW !== undefined) {
58
+ entryW.indegree++;
59
+ entryV.out.push(mappedEntries[e.w]);
60
+ }
61
+ });
62
+
63
+ let sourceSet = Object.values(mappedEntries).filter((entry) => !entry.indegree);
64
+
65
+ return doResolveConflicts(sourceSet);
66
+ }
67
+
68
+ function doResolveConflicts(sourceSet) {
69
+ let entries = [];
70
+
71
+ function handleIn(vEntry) {
72
+ return (uEntry) => {
73
+ if (uEntry.merged) {
74
+ return;
75
+ }
76
+ if (
77
+ uEntry.barycenter === undefined ||
78
+ vEntry.barycenter === undefined ||
79
+ uEntry.barycenter >= vEntry.barycenter
80
+ ) {
81
+ mergeEntries(vEntry, uEntry);
82
+ }
83
+ };
84
+ }
85
+
86
+ function handleOut(vEntry) {
87
+ return (wEntry) => {
88
+ wEntry['in'].push(vEntry);
89
+ if (--wEntry.indegree === 0) {
90
+ sourceSet.push(wEntry);
91
+ }
92
+ };
93
+ }
94
+
95
+ while (sourceSet.length) {
96
+ let entry = sourceSet.pop();
97
+ entries.push(entry);
98
+ entry['in'].reverse().forEach(handleIn(entry));
99
+ entry.out.forEach(handleOut(entry));
100
+ }
101
+
102
+ return entries
103
+ .filter((entry) => !entry.merged)
104
+ .map((entry) => {
105
+ return util.pick(entry, ['vs', 'i', 'barycenter', 'weight']);
106
+ });
107
+ }
108
+
109
+ function mergeEntries(target, source) {
110
+ let sum = 0;
111
+ let weight = 0;
112
+
113
+ if (target.weight) {
114
+ sum += target.barycenter * target.weight;
115
+ weight += target.weight;
116
+ }
117
+
118
+ if (source.weight) {
119
+ sum += source.barycenter * source.weight;
120
+ weight += source.weight;
121
+ }
122
+
123
+ target.vs = source.vs.concat(target.vs);
124
+ target.barycenter = sum / weight;
125
+ target.weight = weight;
126
+ target.i = Math.min(source.i, target.i);
127
+ source.merged = true;
128
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import barycenter from './barycenter';
7
+ import resolveConflicts from './resolve-conflicts';
8
+ import sort from './sort';
9
+
10
+ export { sortSubgraph };
11
+ export default sortSubgraph;
12
+
13
+ function sortSubgraph(g, v, cg, biasRight) {
14
+ let movable = g.children(v);
15
+ let node = g.node(v);
16
+ let bl = node ? node.borderLeft : undefined;
17
+ let br = node ? node.borderRight : undefined;
18
+ let subgraphs = {};
19
+
20
+ if (bl) {
21
+ movable = movable.filter((w) => w !== bl && w !== br);
22
+ }
23
+
24
+ let barycenters = barycenter(g, movable);
25
+ barycenters.forEach((entry) => {
26
+ if (g.children(entry.v).length) {
27
+ let subgraphResult = sortSubgraph(g, entry.v, cg, biasRight);
28
+ subgraphs[entry.v] = subgraphResult;
29
+ if (Object.hasOwn(subgraphResult, 'barycenter')) {
30
+ mergeBarycenters(entry, subgraphResult);
31
+ }
32
+ }
33
+ });
34
+
35
+ let entries = resolveConflicts(barycenters, cg);
36
+ expandSubgraphs(entries, subgraphs);
37
+
38
+ let result = sort(entries, biasRight);
39
+
40
+ if (bl) {
41
+ result.vs = [bl, result.vs, br].flat(true);
42
+ if (g.predecessors(bl).length) {
43
+ let blPred = g.node(g.predecessors(bl)[0]),
44
+ brPred = g.node(g.predecessors(br)[0]);
45
+ if (!Object.hasOwn(result, 'barycenter')) {
46
+ result.barycenter = 0;
47
+ result.weight = 0;
48
+ }
49
+ result.barycenter =
50
+ (result.barycenter * result.weight + blPred.order + brPred.order) / (result.weight + 2);
51
+ result.weight += 2;
52
+ }
53
+ }
54
+
55
+ return result;
56
+ }
57
+
58
+ function expandSubgraphs(entries, subgraphs) {
59
+ entries.forEach((entry) => {
60
+ entry.vs = entry.vs.flatMap((v) => {
61
+ if (subgraphs[v]) {
62
+ return subgraphs[v].vs;
63
+ }
64
+ return v;
65
+ });
66
+ });
67
+ }
68
+
69
+ function mergeBarycenters(target, other) {
70
+ if (target.barycenter !== undefined) {
71
+ target.barycenter =
72
+ (target.barycenter * target.weight + other.barycenter * other.weight) /
73
+ (target.weight + other.weight);
74
+ target.weight += other.weight;
75
+ } else {
76
+ target.barycenter = other.barycenter;
77
+ target.weight = other.weight;
78
+ }
79
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import util from '../util';
7
+
8
+ export { sort };
9
+ export default sort;
10
+
11
+ function sort(entries, biasRight) {
12
+ let parts = util.partition(entries, (entry) => {
13
+ return Object.hasOwn(entry, 'barycenter');
14
+ });
15
+ let sortable = parts.lhs,
16
+ unsortable = parts.rhs.sort((a, b) => b.i - a.i),
17
+ vs = [],
18
+ sum = 0,
19
+ weight = 0,
20
+ vsIndex = 0;
21
+
22
+ sortable.sort(compareWithBias(!!biasRight));
23
+
24
+ vsIndex = consumeUnsortable(vs, unsortable, vsIndex);
25
+
26
+ sortable.forEach((entry) => {
27
+ vsIndex += entry.vs.length;
28
+ vs.push(entry.vs);
29
+ sum += entry.barycenter * entry.weight;
30
+ weight += entry.weight;
31
+ vsIndex = consumeUnsortable(vs, unsortable, vsIndex);
32
+ });
33
+
34
+ let result = { vs: vs.flat(true) };
35
+ if (weight) {
36
+ result.barycenter = sum / weight;
37
+ result.weight = weight;
38
+ }
39
+ return result;
40
+ }
41
+
42
+ function consumeUnsortable(vs, unsortable, index) {
43
+ let last;
44
+ while (unsortable.length && (last = unsortable[unsortable.length - 1]).i <= index) {
45
+ unsortable.pop();
46
+ vs.push(last.vs);
47
+ index++;
48
+ }
49
+ return index;
50
+ }
51
+
52
+ function compareWithBias(bias) {
53
+ return (entryV, entryW) => {
54
+ if (entryV.barycenter < entryW.barycenter) {
55
+ return -1;
56
+ } else if (entryV.barycenter > entryW.barycenter) {
57
+ return 1;
58
+ }
59
+
60
+ return !bias ? entryV.i - entryW.i : entryW.i - entryV.i;
61
+ };
62
+ }