@antv/layout 0.2.0 → 0.2.3
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.LICENSE.txt +0 -9
- package/dist/layout.min.js.map +1 -1
- package/es/layout/circular.js.map +1 -1
- package/es/layout/comboCombined.js +3 -19
- package/es/layout/comboCombined.js.map +1 -1
- package/es/layout/concentric.js.map +1 -1
- package/es/layout/dagre/graph.d.ts +91 -0
- package/es/layout/dagre/graph.js +4 -0
- package/es/layout/dagre/graph.js.map +1 -0
- package/es/layout/dagre/index.d.ts +3 -4
- package/es/layout/dagre/index.js +0 -2
- package/es/layout/dagre/index.js.map +1 -1
- package/es/layout/dagre/src/acyclic.d.ts +1 -2
- package/es/layout/dagre/src/acyclic.js +7 -7
- package/es/layout/dagre/src/acyclic.js.map +1 -1
- package/es/layout/dagre/src/add-border-segments.d.ts +1 -2
- package/es/layout/dagre/src/add-border-segments.js +5 -5
- package/es/layout/dagre/src/add-border-segments.js.map +1 -1
- package/es/layout/dagre/src/coordinate-system.d.ts +1 -2
- package/es/layout/dagre/src/coordinate-system.js +15 -5
- package/es/layout/dagre/src/coordinate-system.js.map +1 -1
- package/es/layout/dagre/src/data/list.d.ts +9 -5
- package/es/layout/dagre/src/data/list.js +25 -27
- package/es/layout/dagre/src/data/list.js.map +1 -1
- package/es/layout/dagre/src/debug.d.ts +2 -3
- package/es/layout/dagre/src/debug.js +3 -5
- package/es/layout/dagre/src/debug.js.map +1 -1
- package/es/layout/dagre/src/greedy-fas.d.ts +2 -3
- package/es/layout/dagre/src/greedy-fas.js +16 -15
- package/es/layout/dagre/src/greedy-fas.js.map +1 -1
- package/es/layout/dagre/src/layout.d.ts +2 -3
- package/es/layout/dagre/src/layout.js +170 -91
- package/es/layout/dagre/src/layout.js.map +1 -1
- package/es/layout/dagre/src/nesting-graph.d.ts +1 -2
- package/es/layout/dagre/src/nesting-graph.js +15 -10
- package/es/layout/dagre/src/nesting-graph.js.map +1 -1
- package/es/layout/dagre/src/normalize.d.ts +1 -2
- package/es/layout/dagre/src/normalize.js +12 -11
- package/es/layout/dagre/src/normalize.js.map +1 -1
- package/es/layout/dagre/src/order/add-subgraph-constraints.d.ts +1 -2
- package/es/layout/dagre/src/order/add-subgraph-constraints.js.map +1 -1
- package/es/layout/dagre/src/order/barycenter.d.ts +1 -2
- package/es/layout/dagre/src/order/barycenter.js.map +1 -1
- package/es/layout/dagre/src/order/build-layer-graph.d.ts +2 -3
- package/es/layout/dagre/src/order/build-layer-graph.js +12 -8
- package/es/layout/dagre/src/order/build-layer-graph.js.map +1 -1
- package/es/layout/dagre/src/order/cross-count.d.ts +2 -3
- package/es/layout/dagre/src/order/cross-count.js +13 -12
- package/es/layout/dagre/src/order/cross-count.js.map +1 -1
- package/es/layout/dagre/src/order/index.d.ts +2 -3
- package/es/layout/dagre/src/order/index.js +17 -15
- package/es/layout/dagre/src/order/index.js.map +1 -1
- package/es/layout/dagre/src/order/init-data-order.d.ts +1 -2
- package/es/layout/dagre/src/order/init-data-order.js +3 -5
- package/es/layout/dagre/src/order/init-data-order.js.map +1 -1
- package/es/layout/dagre/src/order/init-order.d.ts +2 -3
- package/es/layout/dagre/src/order/init-order.js +1 -2
- package/es/layout/dagre/src/order/init-order.js.map +1 -1
- package/es/layout/dagre/src/order/resolve-conflicts.d.ts +18 -3
- package/es/layout/dagre/src/order/resolve-conflicts.js +9 -29
- package/es/layout/dagre/src/order/resolve-conflicts.js.map +1 -1
- package/es/layout/dagre/src/order/sort-subgraph.d.ts +6 -3
- package/es/layout/dagre/src/order/sort-subgraph.js +19 -14
- package/es/layout/dagre/src/order/sort-subgraph.js.map +1 -1
- package/es/layout/dagre/src/order/sort.d.ts +6 -1
- package/es/layout/dagre/src/order/sort.js +2 -2
- package/es/layout/dagre/src/order/sort.js.map +1 -1
- package/es/layout/dagre/src/parent-dummy-chains.d.ts +1 -2
- package/es/layout/dagre/src/parent-dummy-chains.js +47 -44
- package/es/layout/dagre/src/parent-dummy-chains.js.map +1 -1
- package/es/layout/dagre/src/position/bk.d.ts +22 -31
- package/es/layout/dagre/src/position/bk.js +49 -83
- package/es/layout/dagre/src/position/bk.js.map +1 -1
- package/es/layout/dagre/src/position/index.d.ts +1 -2
- package/es/layout/dagre/src/position/index.js +12 -14
- package/es/layout/dagre/src/position/index.js.map +1 -1
- package/es/layout/dagre/src/rank/feasible-tree.d.ts +5 -6
- package/es/layout/dagre/src/rank/feasible-tree.js +1 -2
- package/es/layout/dagre/src/rank/feasible-tree.js.map +1 -1
- package/es/layout/dagre/src/rank/index.d.ts +2 -3
- package/es/layout/dagre/src/rank/index.js.map +1 -1
- package/es/layout/dagre/src/rank/network-simplex.d.ts +8 -11
- package/es/layout/dagre/src/rank/network-simplex.js +18 -31
- package/es/layout/dagre/src/rank/network-simplex.js.map +1 -1
- package/es/layout/dagre/src/rank/util.d.ts +4 -5
- package/es/layout/dagre/src/rank/util.js +37 -20
- package/es/layout/dagre/src/rank/util.js.map +1 -1
- package/es/layout/dagre/src/util.d.ts +29 -48
- package/es/layout/dagre/src/util.js +91 -101
- package/es/layout/dagre/src/util.js.map +1 -1
- package/es/layout/dagre.d.ts +1 -1
- package/es/layout/dagre.js +28 -24
- package/es/layout/dagre.js.map +1 -1
- package/es/layout/fruchterman.js.map +1 -1
- package/es/layout/gForce.js +14 -6
- package/es/layout/gForce.js.map +1 -1
- package/lib/index.js +5 -1
- package/lib/index.js.map +1 -1
- package/lib/layout/circular.js.map +1 -1
- package/lib/layout/comboCombined.js +2 -18
- package/lib/layout/comboCombined.js.map +1 -1
- package/lib/layout/comboForce.js +5 -5
- package/lib/layout/comboForce.js.map +1 -1
- package/lib/layout/concentric.js.map +1 -1
- package/lib/layout/dagre/graph.d.ts +91 -0
- package/lib/layout/dagre/graph.js +28 -0
- package/lib/layout/dagre/graph.js.map +1 -0
- package/lib/layout/dagre/index.d.ts +3 -4
- package/lib/layout/dagre/index.js +0 -2
- package/lib/layout/dagre/index.js.map +1 -1
- package/lib/layout/dagre/src/acyclic.d.ts +1 -2
- package/lib/layout/dagre/src/acyclic.js +7 -7
- package/lib/layout/dagre/src/acyclic.js.map +1 -1
- package/lib/layout/dagre/src/add-border-segments.d.ts +1 -2
- package/lib/layout/dagre/src/add-border-segments.js +5 -8
- package/lib/layout/dagre/src/add-border-segments.js.map +1 -1
- package/lib/layout/dagre/src/coordinate-system.d.ts +1 -2
- package/lib/layout/dagre/src/coordinate-system.js +15 -5
- package/lib/layout/dagre/src/coordinate-system.js.map +1 -1
- package/lib/layout/dagre/src/data/list.d.ts +9 -5
- package/lib/layout/dagre/src/data/list.js +25 -26
- package/lib/layout/dagre/src/data/list.js.map +1 -1
- package/lib/layout/dagre/src/debug.d.ts +2 -3
- package/lib/layout/dagre/src/debug.js +6 -11
- package/lib/layout/dagre/src/debug.js.map +1 -1
- package/lib/layout/dagre/src/greedy-fas.d.ts +2 -3
- package/lib/layout/dagre/src/greedy-fas.js +41 -15
- package/lib/layout/dagre/src/greedy-fas.js.map +1 -1
- package/lib/layout/dagre/src/layout.d.ts +2 -3
- package/lib/layout/dagre/src/layout.js +171 -100
- package/lib/layout/dagre/src/layout.js.map +1 -1
- package/lib/layout/dagre/src/nesting-graph.d.ts +1 -2
- package/lib/layout/dagre/src/nesting-graph.js +15 -13
- package/lib/layout/dagre/src/nesting-graph.js.map +1 -1
- package/lib/layout/dagre/src/normalize.d.ts +1 -2
- package/lib/layout/dagre/src/normalize.js +12 -14
- package/lib/layout/dagre/src/normalize.js.map +1 -1
- package/lib/layout/dagre/src/order/add-subgraph-constraints.d.ts +1 -2
- package/lib/layout/dagre/src/order/add-subgraph-constraints.js.map +1 -1
- package/lib/layout/dagre/src/order/barycenter.d.ts +1 -2
- package/lib/layout/dagre/src/order/barycenter.js.map +1 -1
- package/lib/layout/dagre/src/order/build-layer-graph.d.ts +2 -3
- package/lib/layout/dagre/src/order/build-layer-graph.js +13 -12
- package/lib/layout/dagre/src/order/build-layer-graph.js.map +1 -1
- package/lib/layout/dagre/src/order/cross-count.d.ts +2 -3
- package/lib/layout/dagre/src/order/cross-count.js +14 -13
- package/lib/layout/dagre/src/order/cross-count.js.map +1 -1
- package/lib/layout/dagre/src/order/index.d.ts +2 -3
- package/lib/layout/dagre/src/order/index.js +15 -13
- package/lib/layout/dagre/src/order/index.js.map +1 -1
- package/lib/layout/dagre/src/order/init-data-order.d.ts +1 -2
- package/lib/layout/dagre/src/order/init-data-order.js +3 -5
- package/lib/layout/dagre/src/order/init-data-order.js.map +1 -1
- package/lib/layout/dagre/src/order/init-order.d.ts +2 -3
- package/lib/layout/dagre/src/order/init-order.js +1 -2
- package/lib/layout/dagre/src/order/init-order.js.map +1 -1
- package/lib/layout/dagre/src/order/resolve-conflicts.d.ts +18 -3
- package/lib/layout/dagre/src/order/resolve-conflicts.js +9 -29
- package/lib/layout/dagre/src/order/resolve-conflicts.js.map +1 -1
- package/lib/layout/dagre/src/order/sort-subgraph.d.ts +6 -3
- package/lib/layout/dagre/src/order/sort-subgraph.js +16 -11
- package/lib/layout/dagre/src/order/sort-subgraph.js.map +1 -1
- package/lib/layout/dagre/src/order/sort.d.ts +6 -1
- package/lib/layout/dagre/src/order/sort.js +2 -5
- package/lib/layout/dagre/src/order/sort.js.map +1 -1
- package/lib/layout/dagre/src/parent-dummy-chains.d.ts +1 -2
- package/lib/layout/dagre/src/parent-dummy-chains.js +47 -44
- package/lib/layout/dagre/src/parent-dummy-chains.js.map +1 -1
- package/lib/layout/dagre/src/position/bk.d.ts +22 -31
- package/lib/layout/dagre/src/position/bk.js +75 -85
- package/lib/layout/dagre/src/position/bk.js.map +1 -1
- package/lib/layout/dagre/src/position/index.d.ts +1 -2
- package/lib/layout/dagre/src/position/index.js +14 -17
- package/lib/layout/dagre/src/position/index.js.map +1 -1
- package/lib/layout/dagre/src/rank/feasible-tree.d.ts +5 -6
- package/lib/layout/dagre/src/rank/feasible-tree.js +3 -7
- package/lib/layout/dagre/src/rank/feasible-tree.js.map +1 -1
- package/lib/layout/dagre/src/rank/index.d.ts +2 -3
- package/lib/layout/dagre/src/rank/index.js.map +1 -1
- package/lib/layout/dagre/src/rank/network-simplex.d.ts +8 -11
- package/lib/layout/dagre/src/rank/network-simplex.js +27 -35
- package/lib/layout/dagre/src/rank/network-simplex.js.map +1 -1
- package/lib/layout/dagre/src/rank/util.d.ts +4 -5
- package/lib/layout/dagre/src/rank/util.js +36 -19
- package/lib/layout/dagre/src/rank/util.js.map +1 -1
- package/lib/layout/dagre/src/util.d.ts +29 -48
- package/lib/layout/dagre/src/util.js +80 -92
- package/lib/layout/dagre/src/util.js.map +1 -1
- package/lib/layout/dagre.d.ts +1 -1
- package/lib/layout/dagre.js +27 -23
- package/lib/layout/dagre.js.map +1 -1
- package/lib/layout/er/core.js +5 -1
- package/lib/layout/er/core.js.map +1 -1
- package/lib/layout/force/force-in-a-box.js +7 -3
- package/lib/layout/force/force-in-a-box.js.map +1 -1
- package/lib/layout/force/force.js +5 -1
- package/lib/layout/force/force.js.map +1 -1
- package/lib/layout/force/index.js +5 -1
- package/lib/layout/force/index.js.map +1 -1
- package/lib/layout/fruchterman.js.map +1 -1
- package/lib/layout/gForce.js +10 -2
- package/lib/layout/gForce.js.map +1 -1
- package/lib/layout/grid.js +2 -2
- package/lib/layout/grid.js.map +1 -1
- package/lib/layout/index.js +5 -1
- package/lib/layout/index.js.map +1 -1
- package/lib/layout/radial/index.js +5 -1
- package/lib/layout/radial/index.js.map +1 -1
- package/lib/registy/index.js +1 -1
- package/lib/registy/index.js.map +1 -1
- package/lib/util/index.js +5 -1
- package/lib/util/index.js.map +1 -1
- package/package.json +3 -2
- 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 +390 -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 +391 -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 +421 -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 -78
- package/es/layout/dagre/src/graphlib.d.ts +0 -2
- package/es/layout/dagre/src/graphlib.js +0 -51
- package/es/layout/dagre/src/graphlib.js.map +0 -1
- package/lib/layout/dagre/src/graphlib.d.ts +0 -2
- package/lib/layout/dagre/src/graphlib.js +0 -56
- package/lib/layout/dagre/src/graphlib.js.map +0 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import Grid from './grid';
|
|
2
|
+
import { INode, IEdgeInfo } from './type';
|
|
3
|
+
|
|
4
|
+
export default function layout(data: {
|
|
5
|
+
nodes: INode[],
|
|
6
|
+
edges: IEdgeInfo[],
|
|
7
|
+
}, options: any) {
|
|
8
|
+
if (!data.nodes || data.nodes.length === 0) return data;
|
|
9
|
+
const width = options.width;
|
|
10
|
+
const height = options.height;
|
|
11
|
+
const nodeMinGap = options.nodeMinGap;
|
|
12
|
+
|
|
13
|
+
// 2. 网格布局
|
|
14
|
+
let CELL_W = 10000;
|
|
15
|
+
let CELL_H = 10000;
|
|
16
|
+
data.nodes.forEach((node) => {
|
|
17
|
+
const nodeWidth = node.size[0] || 50;
|
|
18
|
+
const nodeHeight = node.size[1] || 50;
|
|
19
|
+
|
|
20
|
+
CELL_W = Math.min(nodeWidth, CELL_W);
|
|
21
|
+
CELL_H = Math.min(nodeHeight, CELL_H);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const grid = new Grid();
|
|
25
|
+
grid.init(width, height, {
|
|
26
|
+
CELL_H,
|
|
27
|
+
CELL_W,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
data.nodes.forEach((d) => {
|
|
31
|
+
const gridpoint = grid.occupyNearest(d);
|
|
32
|
+
if (gridpoint) {
|
|
33
|
+
gridpoint.node = {
|
|
34
|
+
id: d.id,
|
|
35
|
+
size: d.size,
|
|
36
|
+
};
|
|
37
|
+
d.x = gridpoint.x;
|
|
38
|
+
d.y = gridpoint.y;
|
|
39
|
+
d.dx = gridpoint.dx;
|
|
40
|
+
d.dy = gridpoint.dy;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// 加入节点size
|
|
45
|
+
for (let i = 0; i < data.nodes.length; i++) {
|
|
46
|
+
// 节点宽度大于网格宽度,则往当前网格的右边插入列
|
|
47
|
+
const node = data.nodes[i];
|
|
48
|
+
const result = grid.findGridByNodeId(node.id);
|
|
49
|
+
if (!result) throw new Error("can not find node cell");
|
|
50
|
+
|
|
51
|
+
const { column, row } = result;
|
|
52
|
+
if ((node.size[0] + nodeMinGap) > CELL_W) {
|
|
53
|
+
const addGridSize = Math.ceil((node.size[0] +nodeMinGap) / CELL_W) - 1;
|
|
54
|
+
let realAdd = addGridSize;
|
|
55
|
+
// 优化,假设同一列,不同行存在两个size为2的节点,遍历到第一个节点的时候,会往右插入两列,遍历到第二个节点,又往右插入。就会导致多余的网格
|
|
56
|
+
for(let j=0; j< addGridSize; j++) {
|
|
57
|
+
const hasColumn = grid.additionColumn.indexOf(column + j + 1) > -1;
|
|
58
|
+
if (hasColumn && !grid.cells[column + j + 1][row].node) {
|
|
59
|
+
realAdd --;
|
|
60
|
+
} else {
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
grid.insertColumn(column, realAdd);
|
|
65
|
+
}
|
|
66
|
+
// 节点高度大于网格宽度,则往当前网格的下边插入行
|
|
67
|
+
if ((node.size[1] +nodeMinGap) > CELL_H) {
|
|
68
|
+
const addGridSize = Math.ceil((node.size[1]+nodeMinGap) / CELL_H) - 1;
|
|
69
|
+
let realAdd = addGridSize;
|
|
70
|
+
for(let j=0; j< addGridSize; j++) {
|
|
71
|
+
const hasColumn = grid.additionRow.indexOf(row + j + 1) > -1;
|
|
72
|
+
if (hasColumn && !grid.cells[column][row + j + 1].node) {
|
|
73
|
+
realAdd --;
|
|
74
|
+
} else {
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
grid.insertRow(row, realAdd);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 同步节点坐标
|
|
83
|
+
for(let i = 0; i < grid.columnNum; i++) {
|
|
84
|
+
for(let j = 0; j < grid.rowNum; j++) {
|
|
85
|
+
const cell = grid.cells[i][j];
|
|
86
|
+
if (cell.node) {
|
|
87
|
+
const node = data.nodes.find((node) => node.id === cell?.node?.id);
|
|
88
|
+
if (node) {
|
|
89
|
+
node.x = cell.x + node.size[0] / 2;
|
|
90
|
+
node.y = cell.y + node.size[1] / 2;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { ICell, INode } from './type';
|
|
2
|
+
|
|
3
|
+
export default class Grid {
|
|
4
|
+
public cells: ICell[][] = [];
|
|
5
|
+
public columnNum:number = 0;
|
|
6
|
+
public rowNum: number = 0;
|
|
7
|
+
|
|
8
|
+
public additionColumn: number[] = [];
|
|
9
|
+
public additionRow: number[] = [];
|
|
10
|
+
private static MIN_DIST = 50;
|
|
11
|
+
private static DEFAULT_CELL_W = 80;
|
|
12
|
+
private static DEFAULT_CELL_H = 80;
|
|
13
|
+
private CELL_W: number;
|
|
14
|
+
private CELL_H: number;
|
|
15
|
+
|
|
16
|
+
public init(width: number, height: number, gridSize: {
|
|
17
|
+
CELL_W: number,
|
|
18
|
+
CELL_H: number,
|
|
19
|
+
}) {
|
|
20
|
+
this.cells = [];
|
|
21
|
+
this.CELL_W = gridSize.CELL_W || Grid.DEFAULT_CELL_W;
|
|
22
|
+
this.CELL_H = gridSize.CELL_H || Grid.DEFAULT_CELL_H;
|
|
23
|
+
this.columnNum = Math.ceil(width / this.CELL_W);
|
|
24
|
+
this.rowNum = Math.ceil(height / this.CELL_H);
|
|
25
|
+
Grid.MIN_DIST = Math.pow(width, 2) + Math.pow(height, 2);
|
|
26
|
+
|
|
27
|
+
for(let i = 0; i < this.columnNum; i++) {
|
|
28
|
+
const tmp = [];
|
|
29
|
+
for(let j = 0; j < this.rowNum; j++) {
|
|
30
|
+
const cell = {
|
|
31
|
+
dx: i,
|
|
32
|
+
dy: j,
|
|
33
|
+
x : i * this.CELL_W,
|
|
34
|
+
y : j * this.CELL_H,
|
|
35
|
+
occupied : false
|
|
36
|
+
};
|
|
37
|
+
tmp.push(cell);
|
|
38
|
+
}
|
|
39
|
+
this.cells.push(tmp);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public findGridByNodeId(nodeId: string){
|
|
44
|
+
for(let i = 0; i < this.columnNum; i++) {
|
|
45
|
+
for(let j = 0; j < this.rowNum; j++) {
|
|
46
|
+
if(this.cells[i][j].node) {
|
|
47
|
+
if (this.cells[i][j]?.node?.id === nodeId) {
|
|
48
|
+
return {column: i, row: j};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public sqdist(a: any, b: any) {
|
|
57
|
+
return Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public occupyNearest(p: INode) {
|
|
61
|
+
let minDist = Grid.MIN_DIST;
|
|
62
|
+
let d;
|
|
63
|
+
let candidate = null;
|
|
64
|
+
for(let i = 0; i < this.columnNum; i++) {
|
|
65
|
+
for(let j = 0; j < this.rowNum; j++) {
|
|
66
|
+
if(!this.cells[i][j].occupied && ( d = this.sqdist(p, this.cells[i][j])) < minDist) {
|
|
67
|
+
minDist = d;
|
|
68
|
+
candidate = this.cells[i][j];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if(candidate) {
|
|
73
|
+
candidate.occupied = true;
|
|
74
|
+
}
|
|
75
|
+
return candidate;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public insertColumn(columnIndex: number, length: number) {
|
|
79
|
+
if (length <= 0) return ;
|
|
80
|
+
// 插入空列
|
|
81
|
+
for (let i = 0; i < length; i++) {
|
|
82
|
+
this.cells[i + this.columnNum] = [];
|
|
83
|
+
for(let j = 0; j < this.rowNum; j++) {
|
|
84
|
+
this.cells[i + this.columnNum][j] = {
|
|
85
|
+
dx: i,
|
|
86
|
+
dy: j,
|
|
87
|
+
x : i * this.CELL_W,
|
|
88
|
+
y : j * this.CELL_H,
|
|
89
|
+
occupied : false,
|
|
90
|
+
node: null,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// 交换数据
|
|
95
|
+
for(let i = (this.columnNum - 1); i > columnIndex; i--) {
|
|
96
|
+
for (let j = 0; j < this.rowNum; j++) {
|
|
97
|
+
this.cells[i + length][j] = {
|
|
98
|
+
...this.cells[i][j],
|
|
99
|
+
x: (i+length) * this.CELL_W,
|
|
100
|
+
y: j * this.CELL_H,
|
|
101
|
+
};
|
|
102
|
+
this.cells[i][j] = {
|
|
103
|
+
x : i * this.CELL_W,
|
|
104
|
+
y : j * this.CELL_H,
|
|
105
|
+
occupied : true,
|
|
106
|
+
node: null,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// 已有行列的处理
|
|
111
|
+
for (let j = 0; j < this.additionColumn.length; j++) {
|
|
112
|
+
if (this.additionColumn[j] >= columnIndex) {
|
|
113
|
+
this.additionColumn[j] += length;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// 记录新增的行列
|
|
117
|
+
for (let i = 0; i < length; i++) {
|
|
118
|
+
this.additionColumn.push(columnIndex + i + 1);
|
|
119
|
+
}
|
|
120
|
+
this.columnNum += length;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public insertRow(rowIndex: number, length: number) {
|
|
124
|
+
if (length <= 0) return ;
|
|
125
|
+
// 插入空行
|
|
126
|
+
for (let j = 0; j < length; j++) {
|
|
127
|
+
for(let i = 0; i < this.columnNum; i++) {
|
|
128
|
+
this.cells[i][j + this.rowNum] = {
|
|
129
|
+
dx: i,
|
|
130
|
+
dy: j,
|
|
131
|
+
x : i * this.CELL_W,
|
|
132
|
+
y : j * this.CELL_H,
|
|
133
|
+
occupied : false,
|
|
134
|
+
node: null,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 交换数据
|
|
140
|
+
for(let i = 0; i < this.columnNum; i++) {
|
|
141
|
+
for (let j = (this.rowNum - 1); j > rowIndex; j--) {
|
|
142
|
+
this.cells[i][j+length] = {
|
|
143
|
+
...this.cells[i][j],
|
|
144
|
+
dx: i,
|
|
145
|
+
dy: j + length,
|
|
146
|
+
x: i * this.CELL_W,
|
|
147
|
+
y: (j+length) * this.CELL_H,
|
|
148
|
+
};
|
|
149
|
+
this.cells[i][j] = {
|
|
150
|
+
dx: i,
|
|
151
|
+
dy: j,
|
|
152
|
+
x : i * this.CELL_W,
|
|
153
|
+
y : j *this.CELL_H,
|
|
154
|
+
occupied : false,
|
|
155
|
+
node: null,
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// 已有行列的处理
|
|
162
|
+
for (let j = 0; j < this.additionRow.length; j++) {
|
|
163
|
+
if (this.additionRow[j] >= rowIndex) {
|
|
164
|
+
this.additionRow[j] += length;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// 记录新增的行列
|
|
168
|
+
for (let i = 0; i < length; i++) {
|
|
169
|
+
this.additionRow.push(rowIndex + i + 1);
|
|
170
|
+
}
|
|
171
|
+
this.rowNum += length;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
public getNodes() {
|
|
175
|
+
const nodes = [];
|
|
176
|
+
for(let i = 0; i < this.columnNum; i++) {
|
|
177
|
+
for(let j = 0; j < this.rowNum; j++) {
|
|
178
|
+
if(this.cells[i][j].node) {
|
|
179
|
+
nodes.push(this.cells[i][j]);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return nodes;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileOverview Force Layout Grid Align layout
|
|
3
|
+
* @author wenyanqi
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Base } from "../base";
|
|
7
|
+
import layout from './core';
|
|
8
|
+
import { INode } from './type';
|
|
9
|
+
|
|
10
|
+
export interface ERLayoutOptions {
|
|
11
|
+
type: "er";
|
|
12
|
+
width?: number;
|
|
13
|
+
height?: number;
|
|
14
|
+
nodeMinGap?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class ERLayout extends Base {
|
|
18
|
+
|
|
19
|
+
public width: number = 300;
|
|
20
|
+
public height: number = 300;
|
|
21
|
+
public nodeMinGap: number = 50;
|
|
22
|
+
|
|
23
|
+
/** 迭代结束的回调函数 */
|
|
24
|
+
public onLayoutEnd: () => void = () => { };
|
|
25
|
+
|
|
26
|
+
constructor(options?: any) {
|
|
27
|
+
super();
|
|
28
|
+
if (options) {
|
|
29
|
+
this.updateCfg(options);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public getDefaultCfg() {
|
|
34
|
+
return {
|
|
35
|
+
width: 300,
|
|
36
|
+
height: 300,
|
|
37
|
+
nodeMinGap: 50,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 执行布局
|
|
43
|
+
*/
|
|
44
|
+
public execute() {
|
|
45
|
+
const self = this;
|
|
46
|
+
const nodes = self.nodes;
|
|
47
|
+
const edges = self.edges;
|
|
48
|
+
// 节点初始化,size初始化
|
|
49
|
+
nodes?.forEach((node: INode) => {
|
|
50
|
+
if (!node.size) {
|
|
51
|
+
node.size = [50, 50];
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return layout({
|
|
55
|
+
nodes, edges,
|
|
56
|
+
}, {
|
|
57
|
+
width: this.width,
|
|
58
|
+
height: this.height,
|
|
59
|
+
nodeMinGap: this.nodeMinGap,
|
|
60
|
+
}).then(() => {
|
|
61
|
+
if (self.onLayoutEnd) self.onLayoutEnd();
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public getType() {
|
|
66
|
+
return "er";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { IEdge, IMysqlNode } from './type';
|
|
2
|
+
|
|
3
|
+
const graphWidth = 1200;
|
|
4
|
+
const graphHeight = 800;
|
|
5
|
+
const OVERLAP_QUOT = 10000000;
|
|
6
|
+
const MIN_DIST = 10;
|
|
7
|
+
const M_PI = 3.14159265358979323846;
|
|
8
|
+
const M_PI_2 = 1.57079632679489661923;
|
|
9
|
+
const PI_38 = M_PI * 0.375;
|
|
10
|
+
const PI_58 = M_PI * 0.625;
|
|
11
|
+
const nodeEdgeMap = new Map();
|
|
12
|
+
const CELL_W = 10;
|
|
13
|
+
const CELL_H = 10;
|
|
14
|
+
let T = 0.8;
|
|
15
|
+
const T_MIN = 0.1;
|
|
16
|
+
const R = 0.5;
|
|
17
|
+
|
|
18
|
+
function distanceToNode(node1: IMysqlNode, node2: IMysqlNode, isHoriz: boolean) {
|
|
19
|
+
const x11 = node1.x - node1.size[0] / 2;
|
|
20
|
+
const y11 = node1.y - node1.size[1] / 2;
|
|
21
|
+
const x12 = node1.x + node1.size[0] / 2;
|
|
22
|
+
const y12 = node1.y + node1.size[1] / 2;
|
|
23
|
+
const x21 = node2.x - node2.size[0] / 2;
|
|
24
|
+
const y21 = node2.y - node2.size[1] / 2;
|
|
25
|
+
const x22 = node2.x + node2.size[0] / 2;
|
|
26
|
+
const y22 = node2.y + node2.size[1] / 2;
|
|
27
|
+
|
|
28
|
+
const cx1 = node1.x;
|
|
29
|
+
const cy1 = node1.y;
|
|
30
|
+
const cx2 = node2.x;
|
|
31
|
+
const cy2 = node2.y;
|
|
32
|
+
const dcx = cx2 - cx1;
|
|
33
|
+
// 两个节点间的方位角
|
|
34
|
+
const qr = Math.atan2(dcx, (cy2 - cy1));
|
|
35
|
+
let dx = 0;
|
|
36
|
+
let dy = 0;
|
|
37
|
+
let l1 = 0;
|
|
38
|
+
let l2 = 0;
|
|
39
|
+
if (qr > M_PI_2) {
|
|
40
|
+
dy = y11 - y22;
|
|
41
|
+
dx = x21 - x12;
|
|
42
|
+
l1 = parseFloat(dy ? (dy / Math.cos(qr)).toFixed(2) : (dx).toFixed(2));
|
|
43
|
+
l2 = parseFloat(dx ? (dx / Math.sin(qr)).toFixed(2) : (dy).toFixed(2));
|
|
44
|
+
} else if (0.0 < qr && qr <= M_PI_2) {
|
|
45
|
+
dy = y21 - y12;
|
|
46
|
+
dx = x21 - x12;
|
|
47
|
+
if (dy > dx) {
|
|
48
|
+
l1 = l2 = parseFloat(dy ? (dy / Math.cos(qr)).toFixed(2) : (dx).toFixed(2));
|
|
49
|
+
} else {
|
|
50
|
+
l1 = l2 = parseFloat(dx ? (dx / Math.sin(qr)).toFixed(2) : (dy).toFixed(2));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
} else if (qr < -M_PI_2) {
|
|
54
|
+
dy = y11 - y22;
|
|
55
|
+
dx = -(x22 - x11);
|
|
56
|
+
if (dy > dx) {
|
|
57
|
+
l1 = l2 = parseFloat(dy ? (dy / Math.cos(qr)).toFixed(2) : (dx).toFixed(2));
|
|
58
|
+
} else {
|
|
59
|
+
l1 = l2 = parseFloat(dx ? (dx / Math.sin(qr)).toFixed(2) : (dy).toFixed(2));
|
|
60
|
+
}
|
|
61
|
+
}else {
|
|
62
|
+
dy = y21 - y12;
|
|
63
|
+
if (Math.abs(dcx) > (x12 - x11) / 2) {
|
|
64
|
+
dx = x11 - x22;
|
|
65
|
+
} else {
|
|
66
|
+
dx = dcx;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (dy > dx) {
|
|
70
|
+
l1 = l2 = parseFloat(dy ? (dy / Math.cos(qr)).toFixed(2) : (dx).toFixed(2));
|
|
71
|
+
} else {
|
|
72
|
+
l1 = l2 = parseFloat((dx && qr !== 0.0) ? (dx / Math.sin(qr)).toFixed(2) : (dy).toFixed(2));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
const aqr = parseFloat(qr.toFixed(2));
|
|
77
|
+
// 判断是否水平,角度
|
|
78
|
+
let newHoriz = isHoriz;
|
|
79
|
+
if (isHoriz) {
|
|
80
|
+
newHoriz = PI_38 < aqr && aqr < PI_58;
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
distance: Math.abs(l1 < l2 ? l1 : l2),
|
|
84
|
+
isHoriz: newHoriz,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function calcNodePair(nodeA: IMysqlNode, nodeB: IMysqlNode) {
|
|
89
|
+
// 确定两个节点间是否存在连线
|
|
90
|
+
const edges = nodeEdgeMap.get(nodeA.id) || [];
|
|
91
|
+
const isLinked = edges.find((edge: IEdge) => {
|
|
92
|
+
return edge.source === nodeB.id || edge.target === nodeB.id;
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const areaA = nodeA.size[0] * nodeA.size[1];
|
|
96
|
+
const areaB = nodeB.size[0] * nodeB.size[1];
|
|
97
|
+
const node1 = areaA > areaB ? nodeB : nodeA;
|
|
98
|
+
const node2 = areaA > areaB ? nodeA : nodeB;
|
|
99
|
+
|
|
100
|
+
const x11 = node1.x - node1.size[0] / 2;
|
|
101
|
+
const y11 = node1.y - node1.size[1] / 2;
|
|
102
|
+
const x12 = node1.x + node1.size[0] / 2;
|
|
103
|
+
const y12 = node1.y + node1.size[1] / 2;
|
|
104
|
+
const x21 = node2.x - node2.size[0] / 2;
|
|
105
|
+
const y21 = node2.y - node2.size[1] / 2;
|
|
106
|
+
const x22 = node2.x + node2.size[0] / 2;
|
|
107
|
+
const y22 = node2.y + node2.size[1] / 2;
|
|
108
|
+
|
|
109
|
+
const cx1 = node1.x;
|
|
110
|
+
const cy1 = node1.y;
|
|
111
|
+
const cx2 = node2.x;
|
|
112
|
+
const cy2 = node2.y;
|
|
113
|
+
|
|
114
|
+
// Detect if nodes overlap 检查节点之间是否存在覆盖问题
|
|
115
|
+
const isoverlap = ((x12 >= x21) && (x22 >= x11) && (y12 >= y21) && (y22 >= y11));
|
|
116
|
+
let e = 0;
|
|
117
|
+
let distance = 0;
|
|
118
|
+
|
|
119
|
+
if (isoverlap) {
|
|
120
|
+
|
|
121
|
+
distance = Math.sqrt(Math.pow((cx2 - cx1), 2) + Math.pow((cy2 - cy1), 2));
|
|
122
|
+
|
|
123
|
+
// calc area of overlap 计算重复区域的坐标和面积
|
|
124
|
+
const sx1 = x11 > x21 ? x11 : x21;
|
|
125
|
+
const sy1 = y11 > y21 ? y11 : y21;
|
|
126
|
+
const sx2 = x12 < x22 ? x12 : x22;
|
|
127
|
+
const sy2 = y12 < y22 ? y12 : y22;
|
|
128
|
+
const dsx = sx2 - sx1;
|
|
129
|
+
const dsy = sy2 - sy1;
|
|
130
|
+
|
|
131
|
+
const sov = dsx * dsy;
|
|
132
|
+
|
|
133
|
+
if (distance === 0.0) {
|
|
134
|
+
distance = 0.0000001;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
e = MIN_DIST * 1 / distance * 100 + sov;
|
|
138
|
+
e *= OVERLAP_QUOT;
|
|
139
|
+
} else {
|
|
140
|
+
let isHoriz = false;
|
|
141
|
+
const res = distanceToNode(node1, node2, isHoriz);
|
|
142
|
+
distance = res.distance;
|
|
143
|
+
isHoriz = res.isHoriz;
|
|
144
|
+
|
|
145
|
+
if (distance <= MIN_DIST) {
|
|
146
|
+
if (distance !== 0) {
|
|
147
|
+
if (isLinked) {
|
|
148
|
+
e += MIN_DIST + OVERLAP_QUOT * 1 / distance;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
e += MIN_DIST + OVERLAP_QUOT * MIN_DIST / distance;
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
e += OVERLAP_QUOT;
|
|
155
|
+
}
|
|
156
|
+
} else {
|
|
157
|
+
e += distance;
|
|
158
|
+
if (isLinked) {
|
|
159
|
+
e += distance * distance;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return e;
|
|
165
|
+
}
|
|
166
|
+
function calcEnergy(nodes: any) {
|
|
167
|
+
let energy = 0;
|
|
168
|
+
for(let i = 0; i < nodes.length; i++) {
|
|
169
|
+
const node = nodes[i];
|
|
170
|
+
if ((node.x < 0) || (node.y < 0) || (node.x > graphWidth) || (node.y > graphHeight)) {
|
|
171
|
+
energy += 1000000000000;
|
|
172
|
+
}
|
|
173
|
+
for (let j = i + 1; j < nodes.length; j++) {
|
|
174
|
+
energy += calcNodePair(node, nodes[j]);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return energy;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function isCorrectPosition(node: IMysqlNode, newPosition: {
|
|
182
|
+
x: number, y: number
|
|
183
|
+
}, nodes: IMysqlNode[], edges: IEdge[]) {
|
|
184
|
+
const nodeIdxMap = new Map<string, IMysqlNode>();
|
|
185
|
+
nodes.forEach((o, i) => {
|
|
186
|
+
nodeIdxMap.set(o.id, o);
|
|
187
|
+
});
|
|
188
|
+
const relateEdges = edges.filter((edge) => edge.source === node.id || edge.target === node.id) || [];
|
|
189
|
+
const relateNodes: IMysqlNode[] = [];
|
|
190
|
+
relateEdges.forEach((edge) => {
|
|
191
|
+
const otherNodeId = edge.source === node.id ? edge.target : edge.source;
|
|
192
|
+
const otherNode = nodeIdxMap.get(otherNodeId);
|
|
193
|
+
if (otherNode) {
|
|
194
|
+
relateNodes.push(otherNode);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
let flag = true;
|
|
199
|
+
for(let i = 0; i < relateNodes.length; i++) {
|
|
200
|
+
const item = relateNodes[i];
|
|
201
|
+
// 判断条件调整,节点的坐标不需要完全一致。可以根据节点间的夹角来判断
|
|
202
|
+
const delta = Math.atan((node.y - item.y) / (item.x - node.y)) * 180;
|
|
203
|
+
const newDelta = Math.atan((newPosition.y - item.y) / (item.x - newPosition.y)) * 180;
|
|
204
|
+
const isHor = delta < 30 || delta > 150;
|
|
205
|
+
const newIsHor = newDelta < 30 || newDelta > 150;
|
|
206
|
+
const isVer = delta > 70 && delta < 110;
|
|
207
|
+
const newIsVer = newDelta > 70 && newDelta < 110;
|
|
208
|
+
// 定义四个相似角度区间,0-15度,75-90度,90到105度,165到180度。
|
|
209
|
+
if (isHor && !newIsHor || ((delta * newDelta) < 0)) {
|
|
210
|
+
flag = false;
|
|
211
|
+
break;
|
|
212
|
+
} else if (isVer && !newIsVer || ((delta * newDelta) < 0)) {
|
|
213
|
+
flag = false;
|
|
214
|
+
break;
|
|
215
|
+
} else if ((item.x - node.x) * (item.x - newPosition.x) < 0) {
|
|
216
|
+
flag = false;
|
|
217
|
+
break;
|
|
218
|
+
}else if ((item.y - node.y) * (item.y - newPosition.y) < 0) {
|
|
219
|
+
flag = false;
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return flag;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function shuffle(nodes: IMysqlNode[], edges: IEdge[]) {
|
|
227
|
+
let foundSmallerEnergy = false;
|
|
228
|
+
// 多次测试发现step为1时的效果最佳。
|
|
229
|
+
const step = 1;
|
|
230
|
+
const wstep = CELL_W * step;
|
|
231
|
+
const hstep = CELL_H * step;
|
|
232
|
+
const wsteps = [ wstep, -wstep, 0, 0, ];
|
|
233
|
+
const hsteps = [ 0, 0, hstep, -hstep, ];
|
|
234
|
+
for (let i = 0; i < nodes.length; ++i) {
|
|
235
|
+
const node = nodes[i];
|
|
236
|
+
let nodeEnergy = calcNodeEnergy(node, nodes);
|
|
237
|
+
for (let ns = 0; ns < wsteps.length ; ns++) {
|
|
238
|
+
// 判断新位置与其他连线节点的位置关系是否违规
|
|
239
|
+
const flag = isCorrectPosition(node, { x: node.x + wsteps[ns], y: node.y + hsteps[ns] }, nodes, edges);
|
|
240
|
+
if (flag) {
|
|
241
|
+
// 节点朝上下左右四个方向移动,找到能量最小的那个位置
|
|
242
|
+
node.x += wsteps[ns];
|
|
243
|
+
node.y += hsteps[ns];
|
|
244
|
+
|
|
245
|
+
// 计算移动后节点的能量
|
|
246
|
+
const energy = calcNodeEnergy(node, nodes);
|
|
247
|
+
const rdm = Math.random();
|
|
248
|
+
|
|
249
|
+
if (energy < nodeEnergy) {
|
|
250
|
+
nodeEnergy = energy;
|
|
251
|
+
foundSmallerEnergy = true;
|
|
252
|
+
|
|
253
|
+
} else if (rdm < T && rdm > T_MIN) {
|
|
254
|
+
nodeEnergy = energy;
|
|
255
|
+
foundSmallerEnergy = true;
|
|
256
|
+
|
|
257
|
+
} else {
|
|
258
|
+
// 回归原位
|
|
259
|
+
node.x -= wsteps[ns];
|
|
260
|
+
node.y -= hsteps[ns];
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
}
|
|
266
|
+
if (T > T_MIN) {
|
|
267
|
+
T *= R;
|
|
268
|
+
}
|
|
269
|
+
// 重新计算图整体的能量
|
|
270
|
+
if (foundSmallerEnergy) {
|
|
271
|
+
return calcEnergy(nodes);
|
|
272
|
+
}
|
|
273
|
+
return 0;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// 计算节点的能量,
|
|
277
|
+
function calcNodeEnergy(node: IMysqlNode, nodes: IMysqlNode[]) {
|
|
278
|
+
let e = 0.0;
|
|
279
|
+
if ((node.x < 0) || (node.y < 0) ||
|
|
280
|
+
(node.x + node.size[0] + 20 > graphWidth) ||
|
|
281
|
+
(node.y + node.size[1] + 20 > graphHeight)
|
|
282
|
+
) {
|
|
283
|
+
e += 1000000000000.0;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
for (let i = 0; i < nodes.length; ++i) {
|
|
287
|
+
if (node.id !== nodes[i].id) {
|
|
288
|
+
e += calcNodePair(node, nodes[i]);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return e;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function layout(nodes: IMysqlNode[], edges: IEdge[]) {
|
|
295
|
+
if (nodes.length === 0) {
|
|
296
|
+
return { nodes, edges };
|
|
297
|
+
}
|
|
298
|
+
nodes.forEach((node: any) => {
|
|
299
|
+
const relateEdge = edges.filter((edge) => edge.source === node.id || edge.target === node.id);
|
|
300
|
+
nodeEdgeMap.set(node, relateEdge);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// 1. 初始化
|
|
304
|
+
// 将node按照连接数进行排序
|
|
305
|
+
nodes.sort((node1: IMysqlNode, node2: IMysqlNode) => {
|
|
306
|
+
return nodeEdgeMap.get(node1.id)?.length - nodeEdgeMap.get(node2.id)?.length;
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// 2. 计算图能量
|
|
310
|
+
let minEnergy = calcEnergy(nodes);
|
|
311
|
+
let deSameCount = 20; // de=0 count
|
|
312
|
+
let de = 1; // energy delta
|
|
313
|
+
let prevEnergy = 0;
|
|
314
|
+
// 定义总的迭代次数。超过就停掉,防止死循环
|
|
315
|
+
const MAX_COUNT = 50;
|
|
316
|
+
let count = 0;
|
|
317
|
+
while (deSameCount > 0) {
|
|
318
|
+
count ++;
|
|
319
|
+
if (count >= MAX_COUNT) {
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
const ea = shuffle(nodes, edges);
|
|
323
|
+
if (ea !== 0) {
|
|
324
|
+
prevEnergy = ea;
|
|
325
|
+
}
|
|
326
|
+
de = prevEnergy - minEnergy;
|
|
327
|
+
minEnergy = prevEnergy;
|
|
328
|
+
if (de === 0) {
|
|
329
|
+
--deSameCount;
|
|
330
|
+
} else {
|
|
331
|
+
deSameCount = 20;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
nodes.forEach((node: IMysqlNode) => {
|
|
335
|
+
node.x = node.x - node.size[0] / 2;
|
|
336
|
+
node.y = node.y - node.size[1] / 2;
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
return {
|
|
340
|
+
nodes,
|
|
341
|
+
edges,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export default layout;
|