@antv/layout 0.2.2 → 0.2.5
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/layout.min.js +1 -1
- package/dist/layout.min.js.map +1 -1
- package/es/layout/comboCombined.js +6 -21
- package/es/layout/comboCombined.js.map +1 -1
- package/es/layout/grid.js +2 -0
- package/es/layout/grid.js.map +1 -1
- package/es/layout/types.d.ts +7 -3
- package/lib/layout/comboCombined.js +6 -21
- package/lib/layout/comboCombined.js.map +1 -1
- package/lib/layout/grid.js +2 -0
- package/lib/layout/grid.js.map +1 -1
- package/lib/layout/types.d.ts +7 -3
- package/package.json +2 -1
- package/src/index.ts +7 -0
- package/src/layout/base.ts +54 -0
- package/src/layout/circular.ts +369 -0
- package/src/layout/comboCombined.ts +391 -0
- package/src/layout/comboForce.ts +873 -0
- package/src/layout/concentric.ts +289 -0
- package/src/layout/constants.ts +21 -0
- package/src/layout/dagre/graph.ts +104 -0
- package/src/layout/dagre/index.ts +31 -0
- package/src/layout/dagre/src/acyclic.ts +58 -0
- package/src/layout/dagre/src/add-border-segments.ts +47 -0
- package/src/layout/dagre/src/coordinate-system.ts +77 -0
- package/src/layout/dagre/src/data/list.ts +60 -0
- package/src/layout/dagre/src/debug.ts +30 -0
- package/src/layout/dagre/src/greedy-fas.ts +144 -0
- package/src/layout/dagre/src/layout.ts +580 -0
- package/src/layout/dagre/src/nesting-graph.ts +143 -0
- package/src/layout/dagre/src/normalize.ts +96 -0
- package/src/layout/dagre/src/order/add-subgraph-constraints.ts +29 -0
- package/src/layout/dagre/src/order/barycenter.ts +26 -0
- package/src/layout/dagre/src/order/build-layer-graph.ts +82 -0
- package/src/layout/dagre/src/order/cross-count.ts +77 -0
- package/src/layout/dagre/src/order/index.ts +105 -0
- package/src/layout/dagre/src/order/init-data-order.ts +27 -0
- package/src/layout/dagre/src/order/init-order.ts +56 -0
- package/src/layout/dagre/src/order/resolve-conflicts.ts +152 -0
- package/src/layout/dagre/src/order/sort-subgraph.ts +105 -0
- package/src/layout/dagre/src/order/sort.ts +76 -0
- package/src/layout/dagre/src/parent-dummy-chains.ts +102 -0
- package/src/layout/dagre/src/position/bk.ts +494 -0
- package/src/layout/dagre/src/position/index.ts +82 -0
- package/src/layout/dagre/src/rank/feasible-tree.ts +165 -0
- package/src/layout/dagre/src/rank/index.ts +54 -0
- package/src/layout/dagre/src/rank/network-simplex.ts +225 -0
- package/src/layout/dagre/src/rank/util.ts +157 -0
- package/src/layout/dagre/src/util.ts +308 -0
- package/src/layout/dagre.ts +423 -0
- package/src/layout/dagreCompound.ts +518 -0
- package/src/layout/er/core.ts +117 -0
- package/src/layout/er/forceGrid.ts +95 -0
- package/src/layout/er/grid.ts +185 -0
- package/src/layout/er/index.ts +68 -0
- package/src/layout/er/mysqlWorkbench.ts +345 -0
- package/src/layout/er/type.ts +39 -0
- package/src/layout/force/force-in-a-box.ts +400 -0
- package/src/layout/force/force.ts +391 -0
- package/src/layout/force/index.ts +1 -0
- package/src/layout/forceAtlas2/body.ts +115 -0
- package/src/layout/forceAtlas2/index.ts +556 -0
- package/src/layout/forceAtlas2/quad.ts +115 -0
- package/src/layout/forceAtlas2/quadTree.ts +107 -0
- package/src/layout/fruchterman.ts +361 -0
- package/src/layout/gForce.ts +487 -0
- package/src/layout/gpu/fruchterman.ts +314 -0
- package/src/layout/gpu/fruchtermanShader.ts +204 -0
- package/src/layout/gpu/gForce.ts +406 -0
- package/src/layout/gpu/gForceShader.ts +221 -0
- package/src/layout/grid.ts +393 -0
- package/src/layout/index.ts +45 -0
- package/src/layout/layout.ts +75 -0
- package/src/layout/mds.ts +140 -0
- package/src/layout/radial/index.ts +1 -0
- package/src/layout/radial/mds.ts +51 -0
- package/src/layout/radial/radial.ts +500 -0
- package/src/layout/radial/radialNonoverlapForce.ts +189 -0
- package/src/layout/random.ts +75 -0
- package/src/layout/types.ts +425 -0
- package/src/registy/index.ts +43 -0
- package/src/util/array.ts +1 -0
- package/src/util/function.ts +64 -0
- package/src/util/gpu.ts +254 -0
- package/src/util/index.ts +6 -0
- package/src/util/math.ts +158 -0
- package/src/util/number.ts +8 -0
- package/src/util/object.ts +28 -0
- package/src/util/string.ts +18 -0
- package/CHANGELOG.md +0 -88
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { isNumber } from "../../../util";
|
|
2
|
+
import { Graph, Node } from "../graph";
|
|
3
|
+
|
|
4
|
+
const safeSort = (valueA?: number, valueB?: number) => {
|
|
5
|
+
return Number(valueA) - Number(valueB);
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* Adds a dummy node to the graph and return v.
|
|
10
|
+
*/
|
|
11
|
+
export const addDummyNode = (
|
|
12
|
+
g: Graph,
|
|
13
|
+
type: string,
|
|
14
|
+
attrs: Node<Record<string, any>>,
|
|
15
|
+
name: string
|
|
16
|
+
) => {
|
|
17
|
+
let v;
|
|
18
|
+
do {
|
|
19
|
+
v = `${name}${Math.random()}`;
|
|
20
|
+
} while (g.hasNode(v));
|
|
21
|
+
|
|
22
|
+
attrs.dummy = type;
|
|
23
|
+
g.setNode(v, attrs);
|
|
24
|
+
|
|
25
|
+
return v;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/*
|
|
29
|
+
* Returns a new graph with only simple edges. Handles aggregation of data
|
|
30
|
+
* associated with multi-edges.
|
|
31
|
+
*/
|
|
32
|
+
export const simplify = (g: Graph) => {
|
|
33
|
+
const simplified = new Graph().setGraph(g.graph());
|
|
34
|
+
g.nodes().forEach((v) => { simplified.setNode(v, g.node(v)); });
|
|
35
|
+
g.edges().forEach((e) => {
|
|
36
|
+
const simpleLabel = simplified.edgeFromArgs(e.v, e.w) || { weight: 0, minlen: 1 };
|
|
37
|
+
const label = g.edge(e)!;
|
|
38
|
+
simplified.setEdge(e.v, e.w, {
|
|
39
|
+
weight: simpleLabel.weight! + label.weight!,
|
|
40
|
+
minlen: Math.max(simpleLabel.minlen!, label.minlen!)
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
return simplified;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const asNonCompoundGraph = (g: Graph) => {
|
|
47
|
+
const simplified = new Graph({ multigraph: g.isMultigraph() }).setGraph(
|
|
48
|
+
g.graph()
|
|
49
|
+
);
|
|
50
|
+
g.nodes().forEach((node) => {
|
|
51
|
+
if (!g.children(node)?.length) {
|
|
52
|
+
simplified.setNode(node, g.node(node));
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
g.edges().forEach((edge) => {
|
|
57
|
+
simplified.setEdgeObj(edge, g.edge(edge));
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return simplified;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const zipObject = <T = any>(keys: string[], values: T[]) => {
|
|
64
|
+
return keys?.reduce((obj, key, i) => {
|
|
65
|
+
obj[key] = values[i];
|
|
66
|
+
return obj;
|
|
67
|
+
}, {} as Record<string, T>);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const successorWeights = (g: Graph) => {
|
|
71
|
+
const weightsMap: Record<string, Record<string, number>> = {};
|
|
72
|
+
|
|
73
|
+
g.nodes().forEach((node) => {
|
|
74
|
+
const sucs: Record<string, number> = {};
|
|
75
|
+
g.outEdges(node)?.forEach((e) => {
|
|
76
|
+
sucs[e.w] = (sucs[e.w] || 0) + (g.edge(e)?.weight || 0);
|
|
77
|
+
});
|
|
78
|
+
weightsMap[node] = sucs;
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return weightsMap;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
export const predecessorWeights = (g: Graph) => {
|
|
87
|
+
const nodes = g.nodes();
|
|
88
|
+
|
|
89
|
+
const weightMap = nodes.map((v) => {
|
|
90
|
+
const preds: Record<string, number> = {};
|
|
91
|
+
g.inEdges(v)?.forEach((e) => {
|
|
92
|
+
preds[e.v] = (preds[e.v] || 0) + g.edge(e)!.weight!;
|
|
93
|
+
});
|
|
94
|
+
return preds;
|
|
95
|
+
});
|
|
96
|
+
return zipObject(nodes, weightMap);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/*
|
|
100
|
+
* Finds where a line starting at point ({x, y}) would intersect a rectangle
|
|
101
|
+
* ({x, y, width, height}) if it were pointing at the rectangle's center.
|
|
102
|
+
*/
|
|
103
|
+
export const intersectRect = (
|
|
104
|
+
rect: { x?: number; y?: number; width?: number; height?: number },
|
|
105
|
+
point: { x?: number; y?: number }
|
|
106
|
+
) => {
|
|
107
|
+
const x = Number(rect.x);
|
|
108
|
+
const y = Number(rect.y);
|
|
109
|
+
|
|
110
|
+
// Rectangle intersection algorithm from:
|
|
111
|
+
// http://math.stackexchange.com/questions/108113/find-edge-between-two-boxes
|
|
112
|
+
const dx = Number(point.x) - x;
|
|
113
|
+
const dy = Number(point.y) - y;
|
|
114
|
+
let w = Number(rect.width) / 2;
|
|
115
|
+
let h = Number(rect.height) / 2;
|
|
116
|
+
|
|
117
|
+
if (!dx && !dy) {
|
|
118
|
+
// completely overlapped directly, then return points its self
|
|
119
|
+
return { x: 0, y: 0 };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let sx;
|
|
123
|
+
let sy;
|
|
124
|
+
|
|
125
|
+
if (Math.abs(dy) * w > Math.abs(dx) * h) {
|
|
126
|
+
// Intersection is top or bottom of rect.
|
|
127
|
+
if (dy < 0) {
|
|
128
|
+
h = -h;
|
|
129
|
+
}
|
|
130
|
+
sx = (h * dx) / dy;
|
|
131
|
+
sy = h;
|
|
132
|
+
} else {
|
|
133
|
+
// Intersection is left or right of rect.
|
|
134
|
+
if (dx < 0) {
|
|
135
|
+
w = -w;
|
|
136
|
+
}
|
|
137
|
+
sx = w;
|
|
138
|
+
sy = (w * dy) / dx;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return { x: x + sx, y: y + sy };
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/*
|
|
145
|
+
* Given a DAG with each node assigned "rank" and "order" properties, this
|
|
146
|
+
* const will produce a matrix with the ids of each node.
|
|
147
|
+
*/
|
|
148
|
+
export const buildLayerMatrix = (g: Graph) => {
|
|
149
|
+
const layeringNodes: string[][] = [];
|
|
150
|
+
const rankMax = maxRank(g) + 1;
|
|
151
|
+
for (let i = 0; i < rankMax; i++) {
|
|
152
|
+
layeringNodes.push([]);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// const layering = _.map(_.range(maxRank(g) + 1), function() { return []; });
|
|
156
|
+
g.nodes().forEach((v: string) => {
|
|
157
|
+
const node = g.node(v)!;
|
|
158
|
+
const rank = node.rank;
|
|
159
|
+
if (rank !== undefined && layeringNodes[rank]) {
|
|
160
|
+
layeringNodes[rank].push(v);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
for (let i = 0; i < rankMax; i++) {
|
|
165
|
+
layeringNodes[i] = layeringNodes[i].sort((va: string, vb: string) =>
|
|
166
|
+
safeSort(g.node(va)?.order, g.node(vb)?.order)
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return layeringNodes;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/*
|
|
174
|
+
* Adjusts the ranks for all nodes in the graph such that all nodes v have
|
|
175
|
+
* rank(v) >= 0 and at least one node w has rank(w) = 0.
|
|
176
|
+
*/
|
|
177
|
+
export const normalizeRanks = (g: Graph) => {
|
|
178
|
+
const nodeRanks = g
|
|
179
|
+
.nodes()
|
|
180
|
+
.filter((v) => g.node(v)?.rank !== undefined)
|
|
181
|
+
.map((v) => g.node(v)!.rank!);
|
|
182
|
+
const min = Math.min(...nodeRanks);
|
|
183
|
+
g.nodes().forEach((v) => {
|
|
184
|
+
const node = g.node(v)!;
|
|
185
|
+
if (node.hasOwnProperty("rank") && min !== Infinity) {
|
|
186
|
+
node.rank! -= min;
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
export const removeEmptyRanks = (g: Graph) => {
|
|
192
|
+
// Ranks may not start at 0, so we need to offset them
|
|
193
|
+
const nodes = g.nodes();
|
|
194
|
+
const nodeRanks = nodes
|
|
195
|
+
.filter((v) => g.node(v)?.rank !== undefined)
|
|
196
|
+
.map((v) => g.node(v)!.rank as number);
|
|
197
|
+
|
|
198
|
+
const offset = Math.min(...nodeRanks);
|
|
199
|
+
const layers: string[][] = [];
|
|
200
|
+
|
|
201
|
+
nodes.forEach((v) => {
|
|
202
|
+
const rank = (g.node(v)?.rank || 0) - offset;
|
|
203
|
+
|
|
204
|
+
if (!layers[rank]) {
|
|
205
|
+
layers[rank] = [];
|
|
206
|
+
}
|
|
207
|
+
layers[rank].push(v);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
let delta = 0;
|
|
211
|
+
const nodeRankFactor = g.graph().nodeRankFactor || 0;
|
|
212
|
+
|
|
213
|
+
for (let i = 0; i < layers.length; i++) {
|
|
214
|
+
const vs = layers[i];
|
|
215
|
+
if (vs === undefined) {
|
|
216
|
+
if (i % nodeRankFactor !== 0) {
|
|
217
|
+
delta -= 1;
|
|
218
|
+
}
|
|
219
|
+
} else if (delta) {
|
|
220
|
+
vs?.forEach((v: string) => {
|
|
221
|
+
const node = g.node(v);
|
|
222
|
+
if (node) {
|
|
223
|
+
node.rank = node.rank || 0;
|
|
224
|
+
node.rank += delta;
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
export const addBorderNode = (
|
|
232
|
+
g: Graph,
|
|
233
|
+
prefix: string,
|
|
234
|
+
rank?: number,
|
|
235
|
+
order?: number
|
|
236
|
+
) => {
|
|
237
|
+
const node: Node = {
|
|
238
|
+
width: 0,
|
|
239
|
+
height: 0
|
|
240
|
+
};
|
|
241
|
+
if (isNumber(rank) && isNumber(order)) {
|
|
242
|
+
node.rank = rank;
|
|
243
|
+
node.order = order;
|
|
244
|
+
}
|
|
245
|
+
return addDummyNode(g, "border", node, prefix);
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
export const maxRank = (g: Graph) => {
|
|
249
|
+
let maxRank: number;
|
|
250
|
+
g.nodes().forEach((v) => {
|
|
251
|
+
const rank = g.node(v)?.rank;
|
|
252
|
+
if (rank !== undefined) {
|
|
253
|
+
if (maxRank === undefined || rank > maxRank) {
|
|
254
|
+
maxRank = rank;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
if (!maxRank!) {
|
|
260
|
+
maxRank = 0;
|
|
261
|
+
}
|
|
262
|
+
return maxRank;
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
/*
|
|
266
|
+
* Partition a collection into two groups: `lhs` and `rhs`. If the supplied
|
|
267
|
+
* const returns true for an entry it goes into `lhs`. Otherwise it goes
|
|
268
|
+
* into `rhs.
|
|
269
|
+
*/
|
|
270
|
+
export const partition = <T = any>(
|
|
271
|
+
collection: T[],
|
|
272
|
+
fn: (val: T) => boolean
|
|
273
|
+
) => {
|
|
274
|
+
const result = { lhs: [] as T[], rhs: [] as T[] };
|
|
275
|
+
collection?.forEach((value) => {
|
|
276
|
+
if (fn(value)) {
|
|
277
|
+
result.lhs.push(value);
|
|
278
|
+
} else {
|
|
279
|
+
result.rhs.push(value);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
return result;
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
/*
|
|
286
|
+
* Returns a new const that wraps `fn` with a timer. The wrapper logs the
|
|
287
|
+
* time it takes to execute the function.
|
|
288
|
+
*/
|
|
289
|
+
export const time = (name: string, fn: () => void) => {
|
|
290
|
+
const start = Date.now();
|
|
291
|
+
try {
|
|
292
|
+
return fn();
|
|
293
|
+
} finally {
|
|
294
|
+
console.log(`${name} time: ${Date.now() - start}ms`);
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
export const notime = (name: string, fn: () => void) => {
|
|
299
|
+
return fn();
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
export const minBy = <T = any>(array: T[], func: (param: T) => number) => {
|
|
303
|
+
return array.reduce((a, b) => {
|
|
304
|
+
const valA = func(a);
|
|
305
|
+
const valB = func(b);
|
|
306
|
+
return valA > valB ? b : a;
|
|
307
|
+
});
|
|
308
|
+
};
|