@antv/layout 0.3.16 → 0.3.17-beta.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/dist/layout.min.js +1 -1
- package/dist/layout.min.js.map +1 -1
- package/es/layout/comboCombined.js +1 -1
- package/es/layout/comboCombined.js.map +1 -1
- package/es/layout/dagre/src/position/bk.js +19 -10
- package/es/layout/dagre/src/position/bk.js.map +1 -1
- package/es/layout/dagre/src/rank/feasible-tree.js +1 -1
- package/es/layout/dagre/src/rank/feasible-tree.js.map +1 -1
- package/es/layout/dagre/src/rank/util.js.map +1 -1
- package/es/layout/dagre.js +130 -39
- package/es/layout/dagre.js.map +1 -1
- package/lib/layout/comboCombined.js.map +1 -1
- package/lib/layout/dagre/src/position/bk.js +19 -10
- package/lib/layout/dagre/src/position/bk.js.map +1 -1
- package/lib/layout/dagre/src/rank/feasible-tree.js.map +1 -1
- package/lib/layout/dagre/src/rank/util.js.map +1 -1
- package/lib/layout/dagre.js +126 -35
- package/lib/layout/dagre.js.map +1 -1
- package/package.json +2 -2
- package/src/layout/comboCombined.ts +2 -2
- package/src/layout/dagre/src/position/bk.ts +27 -9
- package/src/layout/dagre/src/rank/feasible-tree.ts +1 -1
- package/src/layout/dagre/src/rank/util.ts +1 -1
- package/src/layout/dagre.ts +252 -77
package/src/layout/dagre.ts
CHANGED
|
@@ -3,11 +3,25 @@
|
|
|
3
3
|
* @author shiwu.wyy@antfin.com
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
Edge,
|
|
8
|
+
OutNode,
|
|
9
|
+
DagreLayoutOptions,
|
|
10
|
+
PointTuple,
|
|
11
|
+
Point,
|
|
12
|
+
Node,
|
|
13
|
+
} from "./types";
|
|
7
14
|
import dagre from "./dagre/index";
|
|
8
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
isArray,
|
|
17
|
+
isNumber,
|
|
18
|
+
isObject,
|
|
19
|
+
getEdgeTerminal,
|
|
20
|
+
getFunc,
|
|
21
|
+
isString,
|
|
22
|
+
} from "../util";
|
|
9
23
|
import { Base } from "./base";
|
|
10
|
-
import { Graph as DagreGraph } from
|
|
24
|
+
import { Graph as DagreGraph } from "./dagre/graph";
|
|
11
25
|
|
|
12
26
|
/**
|
|
13
27
|
* 层次布局
|
|
@@ -57,8 +71,8 @@ export class DagreLayout extends Base {
|
|
|
57
71
|
|
|
58
72
|
/** 上次的布局结果 */
|
|
59
73
|
public preset: {
|
|
60
|
-
nodes: OutNode[]
|
|
61
|
-
edges: any[]
|
|
74
|
+
nodes: OutNode[];
|
|
75
|
+
edges: any[];
|
|
62
76
|
};
|
|
63
77
|
|
|
64
78
|
public nodes: OutNode[] = [];
|
|
@@ -70,7 +84,7 @@ export class DagreLayout extends Base {
|
|
|
70
84
|
|
|
71
85
|
private nodeMap: {
|
|
72
86
|
[id: string]: OutNode;
|
|
73
|
-
}
|
|
87
|
+
};
|
|
74
88
|
|
|
75
89
|
constructor(options?: DagreLayoutOptions) {
|
|
76
90
|
super();
|
|
@@ -101,14 +115,23 @@ export class DagreLayout extends Base {
|
|
|
101
115
|
return layout;
|
|
102
116
|
}
|
|
103
117
|
return true;
|
|
104
|
-
}
|
|
118
|
+
};
|
|
105
119
|
|
|
106
120
|
/**
|
|
107
121
|
* 执行布局
|
|
108
122
|
*/
|
|
109
123
|
public execute() {
|
|
110
124
|
const self = this;
|
|
111
|
-
const {
|
|
125
|
+
const {
|
|
126
|
+
nodes,
|
|
127
|
+
nodeSize,
|
|
128
|
+
rankdir,
|
|
129
|
+
combos,
|
|
130
|
+
begin,
|
|
131
|
+
radial,
|
|
132
|
+
comboEdges = [],
|
|
133
|
+
vedges = [],
|
|
134
|
+
} = self;
|
|
112
135
|
if (!nodes) return;
|
|
113
136
|
const edges = (self.edges as any[]) || [];
|
|
114
137
|
const g = new DagreGraph({
|
|
@@ -119,12 +142,12 @@ export class DagreLayout extends Base {
|
|
|
119
142
|
// collect the nodes in their combo, to create virtual edges for comboEdges
|
|
120
143
|
self.nodeMap = {};
|
|
121
144
|
const nodeComboMap = {} as any;
|
|
122
|
-
nodes.forEach(node => {
|
|
145
|
+
nodes.forEach((node) => {
|
|
123
146
|
self.nodeMap[node.id] = node;
|
|
124
147
|
if (!node.comboId) return;
|
|
125
148
|
nodeComboMap[node.comboId] = nodeComboMap[node.comboId] || [];
|
|
126
149
|
nodeComboMap[node.comboId].push(node.id);
|
|
127
|
-
})
|
|
150
|
+
});
|
|
128
151
|
|
|
129
152
|
let nodeSizeFunc: (d?: any) => number[];
|
|
130
153
|
if (!nodeSize) {
|
|
@@ -132,7 +155,8 @@ export class DagreLayout extends Base {
|
|
|
132
155
|
if (d.size) {
|
|
133
156
|
if (isArray(d.size)) {
|
|
134
157
|
return d.size;
|
|
135
|
-
}
|
|
158
|
+
}
|
|
159
|
+
if (isObject(d.size)) {
|
|
136
160
|
return [d.size.width || 40, d.size.height || 40];
|
|
137
161
|
}
|
|
138
162
|
return [d.size, d.size];
|
|
@@ -178,35 +202,35 @@ export class DagreLayout extends Base {
|
|
|
178
202
|
});
|
|
179
203
|
}
|
|
180
204
|
|
|
181
|
-
nodes
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (this.sortByCombo && node.comboId) {
|
|
196
|
-
if (!comboMap[node.comboId]) {
|
|
197
|
-
comboMap[node.comboId] = { id: node.comboId };
|
|
198
|
-
g.setNode(node.comboId, {});
|
|
205
|
+
nodes
|
|
206
|
+
.filter((node) => node.layout !== false)
|
|
207
|
+
.forEach((node) => {
|
|
208
|
+
const size = nodeSizeFunc(node);
|
|
209
|
+
const verti = vertisep(node);
|
|
210
|
+
const hori = horisep(node);
|
|
211
|
+
const width = size[0] + 2 * hori;
|
|
212
|
+
const height = size[1] + 2 * verti;
|
|
213
|
+
const layer = node.layer;
|
|
214
|
+
if (isNumber(layer)) {
|
|
215
|
+
// 如果有layer属性,加入到node的label中
|
|
216
|
+
g.setNode(node.id, { width, height, layer });
|
|
217
|
+
} else {
|
|
218
|
+
g.setNode(node.id, { width, height });
|
|
199
219
|
}
|
|
200
|
-
g.setParent(node.id, node.comboId);
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
|
|
204
220
|
|
|
221
|
+
if (this.sortByCombo && node.comboId) {
|
|
222
|
+
if (!comboMap[node.comboId]) {
|
|
223
|
+
comboMap[node.comboId] = { id: node.comboId };
|
|
224
|
+
g.setNode(node.comboId, {});
|
|
225
|
+
}
|
|
226
|
+
g.setParent(node.id, node.comboId);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
205
229
|
|
|
206
230
|
edges.forEach((edge) => {
|
|
207
231
|
// dagrejs Wiki https://github.com/dagrejs/dagre/wiki#configuring-the-layout
|
|
208
|
-
const source = getEdgeTerminal(edge,
|
|
209
|
-
const target = getEdgeTerminal(edge,
|
|
232
|
+
const source = getEdgeTerminal(edge, "source");
|
|
233
|
+
const target = getEdgeTerminal(edge, "target");
|
|
210
234
|
if (this.layoutNode(source) && this.layoutNode(target)) {
|
|
211
235
|
g.setEdge(source, target, {
|
|
212
236
|
weight: edge.weight || 1,
|
|
@@ -215,18 +239,22 @@ export class DagreLayout extends Base {
|
|
|
215
239
|
});
|
|
216
240
|
|
|
217
241
|
// create virtual edges from node to node for comboEdges
|
|
218
|
-
|
|
242
|
+
comboEdges?.concat(vedges || [])?.forEach((comboEdge: any) => {
|
|
219
243
|
const { source, target } = comboEdge;
|
|
220
|
-
const sources = comboMap[source]?.collapsed
|
|
221
|
-
|
|
244
|
+
const sources = comboMap[source]?.collapsed
|
|
245
|
+
? [source]
|
|
246
|
+
: nodeComboMap[source] || [source];
|
|
247
|
+
const targets = comboMap[target]?.collapsed
|
|
248
|
+
? [target]
|
|
249
|
+
: nodeComboMap[target] || [target];
|
|
222
250
|
sources.forEach((s: string) => {
|
|
223
251
|
targets.forEach((t: string) => {
|
|
224
252
|
g.setEdge(s, t, {
|
|
225
253
|
weight: comboEdge.weight || 1,
|
|
226
254
|
});
|
|
227
|
-
})
|
|
228
|
-
})
|
|
229
|
-
})
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
});
|
|
230
258
|
|
|
231
259
|
// 考虑增量图中的原始图
|
|
232
260
|
let prevGraph: DagreGraph | undefined = undefined;
|
|
@@ -267,15 +295,15 @@ export class DagreLayout extends Base {
|
|
|
267
295
|
dBegin[1] = begin[1] - minY;
|
|
268
296
|
}
|
|
269
297
|
|
|
298
|
+
const isHorizontal = rankdir === "LR" || rankdir === "RL";
|
|
270
299
|
// 变形为辐射
|
|
271
300
|
if (radial) {
|
|
272
301
|
const { focusNode, ranksep, getRadialPos } = this;
|
|
273
|
-
const focusId = isString(focusNode) ? focusNode: focusNode?.id;
|
|
302
|
+
const focusId = isString(focusNode) ? focusNode : focusNode?.id;
|
|
274
303
|
const focusLayer = focusId ? g.node(focusId)?._rank : 0;
|
|
275
304
|
const layers: any[] = [];
|
|
276
|
-
const
|
|
277
|
-
const
|
|
278
|
-
const sizeDim = isHorizontal ? 'height' : 'width';
|
|
305
|
+
const dim = isHorizontal ? "y" : "x";
|
|
306
|
+
const sizeDim = isHorizontal ? "height" : "width";
|
|
279
307
|
// 找到整个图作为环的坐标维度(dim)的最大、最小值,考虑节点宽度
|
|
280
308
|
let min = Infinity;
|
|
281
309
|
let max = -Infinity;
|
|
@@ -286,22 +314,57 @@ export class DagreLayout extends Base {
|
|
|
286
314
|
const currentNodesep = nodesepfunc(nodes[i]);
|
|
287
315
|
|
|
288
316
|
if (focusLayer === 0) {
|
|
289
|
-
if (!layers[coord._rank])
|
|
317
|
+
if (!layers[coord._rank]) {
|
|
318
|
+
layers[coord._rank] = {
|
|
319
|
+
nodes: [],
|
|
320
|
+
totalWidth: 0,
|
|
321
|
+
maxSize: -Infinity,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
290
324
|
layers[coord._rank].nodes.push(node);
|
|
291
325
|
layers[coord._rank].totalWidth += currentNodesep * 2 + coord[sizeDim];
|
|
292
|
-
if (
|
|
326
|
+
if (
|
|
327
|
+
layers[coord._rank].maxSize < Math.max(coord.width, coord.height)
|
|
328
|
+
) {
|
|
329
|
+
layers[coord._rank].maxSize = Math.max(coord.width, coord.height);
|
|
330
|
+
}
|
|
293
331
|
} else {
|
|
294
332
|
const diffLayer = coord._rank - focusLayer!;
|
|
295
333
|
if (diffLayer === 0) {
|
|
296
|
-
if (!layers[diffLayer])
|
|
334
|
+
if (!layers[diffLayer]) {
|
|
335
|
+
layers[diffLayer] = {
|
|
336
|
+
nodes: [],
|
|
337
|
+
totalWidth: 0,
|
|
338
|
+
maxSize: -Infinity,
|
|
339
|
+
};
|
|
340
|
+
}
|
|
297
341
|
layers[diffLayer].nodes.push(node);
|
|
298
342
|
layers[diffLayer].totalWidth += currentNodesep * 2 + coord[sizeDim];
|
|
299
|
-
if (
|
|
343
|
+
if (
|
|
344
|
+
layers[diffLayer].maxSize < Math.max(coord.width, coord.height)
|
|
345
|
+
) {
|
|
346
|
+
layers[diffLayer].maxSize = Math.max(coord.width, coord.height);
|
|
347
|
+
}
|
|
300
348
|
} else {
|
|
301
349
|
const diffLayerAbs = Math.abs(diffLayer);
|
|
302
|
-
if (!layers[diffLayerAbs])
|
|
303
|
-
|
|
304
|
-
|
|
350
|
+
if (!layers[diffLayerAbs]) {
|
|
351
|
+
layers[diffLayerAbs] = {
|
|
352
|
+
left: [],
|
|
353
|
+
right: [],
|
|
354
|
+
totalWidth: 0,
|
|
355
|
+
maxSize: -Infinity,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
layers[diffLayerAbs].totalWidth +=
|
|
359
|
+
currentNodesep * 2 + coord[sizeDim];
|
|
360
|
+
if (
|
|
361
|
+
layers[diffLayerAbs].maxSize < Math.max(coord.width, coord.height)
|
|
362
|
+
) {
|
|
363
|
+
layers[diffLayerAbs].maxSize = Math.max(
|
|
364
|
+
coord.width,
|
|
365
|
+
coord.height
|
|
366
|
+
);
|
|
367
|
+
}
|
|
305
368
|
if (diffLayer < 0) {
|
|
306
369
|
layers[diffLayerAbs].left.push(node);
|
|
307
370
|
} else {
|
|
@@ -321,16 +384,30 @@ export class DagreLayout extends Base {
|
|
|
321
384
|
|
|
322
385
|
// 扩大最大最小值范围,以便为环上留出接缝处的空隙
|
|
323
386
|
const rangeLength = (max - min) / 0.9;
|
|
324
|
-
const range = [
|
|
387
|
+
const range = [
|
|
388
|
+
(min + max - rangeLength) * 0.5,
|
|
389
|
+
(min + max + rangeLength) * 0.5,
|
|
390
|
+
];
|
|
325
391
|
|
|
326
392
|
// 根据半径、分布比例,计算节点在环上的位置,并返回该组节点中最大的 ranksep 值
|
|
327
|
-
const processNodes = (
|
|
393
|
+
const processNodes = (
|
|
394
|
+
layerNodes: any,
|
|
395
|
+
radius: number,
|
|
396
|
+
propsMaxRanksep = -Infinity,
|
|
397
|
+
arcRange = [0, 1]
|
|
398
|
+
) => {
|
|
328
399
|
let maxRanksep = propsMaxRanksep;
|
|
329
400
|
layerNodes.forEach((node: any) => {
|
|
330
401
|
const coord = g.node(node);
|
|
331
402
|
radiusMap[node] = radius;
|
|
332
403
|
// 获取变形为 radial 后的直角坐标系坐标
|
|
333
|
-
const { x: newX, y: newY } = getRadialPos(
|
|
404
|
+
const { x: newX, y: newY } = getRadialPos(
|
|
405
|
+
coord![dim]!,
|
|
406
|
+
range,
|
|
407
|
+
rangeLength,
|
|
408
|
+
radius,
|
|
409
|
+
arcRange
|
|
410
|
+
);
|
|
334
411
|
// 将新坐标写入源数据
|
|
335
412
|
const i = nodes.findIndex((it) => it.id === node);
|
|
336
413
|
if (!nodes[i]) return;
|
|
@@ -349,7 +426,13 @@ export class DagreLayout extends Base {
|
|
|
349
426
|
let isFirstLevel = true;
|
|
350
427
|
const lastLayerMaxNodeSize = 0;
|
|
351
428
|
layers.forEach((layerNodes) => {
|
|
352
|
-
if (
|
|
429
|
+
if (
|
|
430
|
+
!layerNodes?.nodes?.length &&
|
|
431
|
+
!layerNodes?.left?.length &&
|
|
432
|
+
!layerNodes?.right?.length
|
|
433
|
+
) {
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
353
436
|
// 第一层只有一个节点,直接放在圆心,初始半径设定为 0
|
|
354
437
|
if (isFirstLevel && layerNodes.nodes.length === 1) {
|
|
355
438
|
// 将新坐标写入源数据
|
|
@@ -365,14 +448,27 @@ export class DagreLayout extends Base {
|
|
|
365
448
|
|
|
366
449
|
// 为接缝留出空隙,半径也需要扩大
|
|
367
450
|
radius = Math.max(radius, layerNodes.totalWidth / (2 * Math.PI)); // / 0.9;
|
|
368
|
-
|
|
451
|
+
|
|
369
452
|
let maxRanksep = -Infinity;
|
|
370
453
|
if (focusLayer === 0 || layerNodes.nodes?.length) {
|
|
371
|
-
maxRanksep = processNodes(
|
|
454
|
+
maxRanksep = processNodes(
|
|
455
|
+
layerNodes.nodes,
|
|
456
|
+
radius,
|
|
457
|
+
maxRanksep,
|
|
458
|
+
[0, 1]
|
|
459
|
+
); // 0.8
|
|
372
460
|
} else {
|
|
373
|
-
const leftRatio =
|
|
374
|
-
|
|
375
|
-
|
|
461
|
+
const leftRatio =
|
|
462
|
+
layerNodes.left?.length /
|
|
463
|
+
(layerNodes.left?.length + layerNodes.right?.length);
|
|
464
|
+
maxRanksep = processNodes(layerNodes.left, radius, maxRanksep, [
|
|
465
|
+
0,
|
|
466
|
+
leftRatio,
|
|
467
|
+
]); // 接缝留出 0.05 的缝隙
|
|
468
|
+
maxRanksep = processNodes(layerNodes.right, radius, maxRanksep, [
|
|
469
|
+
leftRatio + 0.05,
|
|
470
|
+
1,
|
|
471
|
+
]); // 接缝留出 0.05 的缝隙
|
|
376
472
|
}
|
|
377
473
|
radius += maxRanksep;
|
|
378
474
|
isFirstLevel = false;
|
|
@@ -381,33 +477,54 @@ export class DagreLayout extends Base {
|
|
|
381
477
|
g.edges().forEach((edge: any) => {
|
|
382
478
|
const coord = g.edge(edge);
|
|
383
479
|
const i = edges.findIndex((it) => {
|
|
384
|
-
const source = getEdgeTerminal(it,
|
|
385
|
-
const target = getEdgeTerminal(it,
|
|
480
|
+
const source = getEdgeTerminal(it, "source");
|
|
481
|
+
const target = getEdgeTerminal(it, "target");
|
|
386
482
|
return source === edge.v && target === edge.w;
|
|
387
483
|
});
|
|
388
484
|
if (i <= -1) return;
|
|
389
|
-
if (
|
|
390
|
-
|
|
391
|
-
|
|
485
|
+
if (
|
|
486
|
+
self.edgeLabelSpace &&
|
|
487
|
+
self.controlPoints &&
|
|
488
|
+
edges[i].type !== "loop"
|
|
489
|
+
) {
|
|
490
|
+
const otherDim = dim === "x" ? "y" : "x";
|
|
491
|
+
const controlPoints = coord?.points?.slice(
|
|
492
|
+
1,
|
|
493
|
+
coord.points.length - 1
|
|
494
|
+
);
|
|
392
495
|
const newControlPoints: Point[] = [];
|
|
393
496
|
const sourceOtherDimValue = g.node(edge.v)?.[otherDim]!;
|
|
394
|
-
const otherDimDist =
|
|
497
|
+
const otherDimDist =
|
|
498
|
+
sourceOtherDimValue - g.node(edge.w)?.[otherDim]!;
|
|
395
499
|
const sourceRadius = radiusMap[edge.v];
|
|
396
500
|
const radiusDist = sourceRadius - radiusMap[edge.w];
|
|
397
501
|
controlPoints?.forEach((point: any) => {
|
|
398
502
|
// 根据该边的起点、终点半径,及起点、终点、控制点位置关系,确定该控制点的半径
|
|
399
|
-
const cRadius =
|
|
503
|
+
const cRadius =
|
|
504
|
+
((point[otherDim] - sourceOtherDimValue) / otherDimDist) *
|
|
505
|
+
radiusDist +
|
|
506
|
+
sourceRadius;
|
|
400
507
|
// 获取变形为 radial 后的直角坐标系坐标
|
|
401
|
-
const newPos = getRadialPos(
|
|
508
|
+
const newPos = getRadialPos(
|
|
509
|
+
point[dim],
|
|
510
|
+
range,
|
|
511
|
+
rangeLength,
|
|
512
|
+
cRadius
|
|
513
|
+
);
|
|
402
514
|
newControlPoints.push({
|
|
403
515
|
x: newPos.x + dBegin[0],
|
|
404
|
-
y: newPos.y + dBegin[1]
|
|
516
|
+
y: newPos.y + dBegin[1],
|
|
405
517
|
});
|
|
406
518
|
});
|
|
407
519
|
edges[i].controlPoints = newControlPoints;
|
|
408
520
|
}
|
|
409
521
|
});
|
|
410
522
|
} else {
|
|
523
|
+
const layerCoords: Set<number> = new Set();
|
|
524
|
+
const isInvert = rankdir === "BT" || rankdir === "RL";
|
|
525
|
+
const layerCoordSort = isInvert
|
|
526
|
+
? (a: number, b: number) => b - a
|
|
527
|
+
: (a: number, b: number) => a - b;
|
|
411
528
|
g.nodes().forEach((node: any) => {
|
|
412
529
|
const coord = g.node(node)!;
|
|
413
530
|
if (!coord) return;
|
|
@@ -420,17 +537,69 @@ export class DagreLayout extends Base {
|
|
|
420
537
|
ndata.y = coord.y! + dBegin[1];
|
|
421
538
|
// @ts-ignore: pass layer order to data for increment layout use
|
|
422
539
|
ndata._order = coord._order;
|
|
540
|
+
layerCoords.add(isHorizontal ? ndata.x : ndata.y);
|
|
423
541
|
});
|
|
542
|
+
const layerCoordsArr = Array.from(layerCoords).sort(layerCoordSort);
|
|
543
|
+
|
|
424
544
|
g.edges().forEach((edge: any) => {
|
|
425
545
|
const coord = g.edge(edge);
|
|
426
546
|
const i = edges.findIndex((it) => {
|
|
427
|
-
const source = getEdgeTerminal(it,
|
|
428
|
-
const target = getEdgeTerminal(it,
|
|
547
|
+
const source = getEdgeTerminal(it, "source");
|
|
548
|
+
const target = getEdgeTerminal(it, "target");
|
|
429
549
|
return source === edge.v && target === edge.w;
|
|
430
550
|
});
|
|
431
551
|
if (i <= -1) return;
|
|
432
|
-
if (
|
|
433
|
-
|
|
552
|
+
if (
|
|
553
|
+
self.edgeLabelSpace &&
|
|
554
|
+
self.controlPoints &&
|
|
555
|
+
edges[i].type !== "loop"
|
|
556
|
+
) {
|
|
557
|
+
edges[i].controlPoints =
|
|
558
|
+
coord?.points?.slice(1, coord.points.length - 1) || []; // 去掉头尾
|
|
559
|
+
|
|
560
|
+
const sourceNode = self.nodeMap[edge.v];
|
|
561
|
+
const targetNode = self.nodeMap[edge.w];
|
|
562
|
+
|
|
563
|
+
// 酌情增加控制点,使折线不穿过跨层的节点
|
|
564
|
+
if (sourceNode && targetNode) {
|
|
565
|
+
let { x: sourceX, y: sourceY } = sourceNode;
|
|
566
|
+
let { x: targetX, y: targetY } = targetNode;
|
|
567
|
+
if (isHorizontal) {
|
|
568
|
+
sourceX = sourceNode.y;
|
|
569
|
+
sourceY = sourceNode.x;
|
|
570
|
+
targetX = targetNode.y;
|
|
571
|
+
targetY = targetNode.x;
|
|
572
|
+
}
|
|
573
|
+
// 为跨层级的边增加第一个控制点。忽略垂直的/横向的边。
|
|
574
|
+
// 新控制点 = {
|
|
575
|
+
// x: 终点x,
|
|
576
|
+
// y: (起点y + 下一层y) / 2, #下一层y可能不等于终点y
|
|
577
|
+
// }
|
|
578
|
+
if (targetY !== sourceY && sourceX !== targetX) {
|
|
579
|
+
const nextLayerCoord =
|
|
580
|
+
layerCoordsArr[layerCoordsArr.indexOf(sourceY) + 1];
|
|
581
|
+
if (nextLayerCoord) {
|
|
582
|
+
const firstControlPoint = edges[i].controlPoints[0];
|
|
583
|
+
const insertControlPoint = isHorizontal
|
|
584
|
+
? {
|
|
585
|
+
x: (sourceY + nextLayerCoord) / 2,
|
|
586
|
+
y: firstControlPoint?.x || targetX,
|
|
587
|
+
}
|
|
588
|
+
: {
|
|
589
|
+
x: firstControlPoint?.x || targetX,
|
|
590
|
+
y: (sourceY + nextLayerCoord) / 2,
|
|
591
|
+
};
|
|
592
|
+
// 当新增的控制点不存在(!=当前第一个控制点)时添加
|
|
593
|
+
if (
|
|
594
|
+
!firstControlPoint ||
|
|
595
|
+
firstControlPoint.y !== insertControlPoint.y
|
|
596
|
+
) {
|
|
597
|
+
edges[i].controlPoints.unshift(insertControlPoint);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
434
603
|
edges[i].controlPoints.forEach((point: any) => {
|
|
435
604
|
point.x += dBegin[0];
|
|
436
605
|
point.y += dBegin[1];
|
|
@@ -446,7 +615,13 @@ export class DagreLayout extends Base {
|
|
|
446
615
|
};
|
|
447
616
|
}
|
|
448
617
|
|
|
449
|
-
private getRadialPos(
|
|
618
|
+
private getRadialPos(
|
|
619
|
+
dimValue: number,
|
|
620
|
+
range: number[],
|
|
621
|
+
rangeLength: number,
|
|
622
|
+
radius: number,
|
|
623
|
+
arcRange: number[] = [0, 1]
|
|
624
|
+
) {
|
|
450
625
|
// dimRatio 占圆弧的比例
|
|
451
626
|
let dimRatio = (dimValue - range[0]) / rangeLength;
|
|
452
627
|
// 再进一步归一化到指定的范围上
|
|
@@ -456,11 +631,11 @@ export class DagreLayout extends Base {
|
|
|
456
631
|
// 将极坐标系转换为直角坐标系
|
|
457
632
|
return {
|
|
458
633
|
x: Math.cos(angle) * radius,
|
|
459
|
-
y: Math.sin(angle) * radius
|
|
634
|
+
y: Math.sin(angle) * radius,
|
|
460
635
|
};
|
|
461
636
|
}
|
|
462
637
|
|
|
463
638
|
public getType() {
|
|
464
639
|
return "dagre";
|
|
465
640
|
}
|
|
466
|
-
}
|
|
641
|
+
}
|