@logicflow/layout 2.1.0-alpha.2 → 2.1.0-alpha.4
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/.turbo/turbo-build.log +7 -6
- package/CHANGELOG.md +15 -0
- package/README.md +9 -2
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/es/{dagre.d.ts → dagre/index.d.ts} +18 -20
- package/es/dagre/index.js +126 -0
- package/es/dagre/index.js.map +1 -0
- package/es/elkLayout/config.d.ts +26 -0
- package/es/elkLayout/config.js +27 -0
- package/es/elkLayout/config.js.map +1 -0
- package/es/elkLayout/index.d.ts +107 -0
- package/es/elkLayout/index.js +187 -0
- package/es/elkLayout/index.js.map +1 -0
- package/es/index.d.ts +1 -0
- package/es/index.js +1 -0
- package/es/index.js.map +1 -1
- package/es/utils/processEdge.d.ts +3 -0
- package/es/utils/processEdge.js +479 -0
- package/es/utils/processEdge.js.map +1 -0
- package/lib/{dagre.d.ts → dagre/index.d.ts} +18 -20
- package/lib/dagre/index.js +152 -0
- package/lib/dagre/index.js.map +1 -0
- package/lib/elkLayout/config.d.ts +26 -0
- package/lib/elkLayout/config.js +30 -0
- package/lib/elkLayout/config.js.map +1 -0
- package/lib/elkLayout/index.d.ts +107 -0
- package/lib/elkLayout/index.js +193 -0
- package/lib/elkLayout/index.js.map +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/utils/processEdge.d.ts +3 -0
- package/lib/utils/processEdge.js +483 -0
- package/lib/utils/processEdge.js.map +1 -0
- package/package.json +3 -2
- package/src/dagre/index.ts +177 -0
- package/src/elkLayout/config.ts +26 -0
- package/src/elkLayout/index.ts +255 -0
- package/src/index.ts +2 -0
- package/src/utils/processEdge.ts +585 -0
- package/stats.html +1 -1
- package/es/dagre.js +0 -376
- package/es/dagre.js.map +0 -1
- package/lib/dagre.js +0 -402
- package/lib/dagre.js.map +0 -1
- package/src/dagre.ts +0 -438
|
@@ -0,0 +1,585 @@
|
|
|
1
|
+
import LogicFlow, { BaseEdgeModel } from '@logicflow/core'
|
|
2
|
+
import NodeConfig = LogicFlow.NodeConfig
|
|
3
|
+
import EdgeConfig = LogicFlow.EdgeConfig
|
|
4
|
+
import Direction = LogicFlow.Direction
|
|
5
|
+
import Point = LogicFlow.Point
|
|
6
|
+
|
|
7
|
+
type BaseNodeData = {
|
|
8
|
+
x: number
|
|
9
|
+
y: number
|
|
10
|
+
width: number
|
|
11
|
+
height: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type IBezierControls = {
|
|
15
|
+
sNext: Point
|
|
16
|
+
ePre: Point
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 定义边界数据结构,左上坐标 + 右下坐标定位一个矩形
|
|
20
|
+
type BoxBoundsPoint = {
|
|
21
|
+
minX: number // Left Top X
|
|
22
|
+
minY: number // Left Top Y
|
|
23
|
+
maxX: number // Right Bottom X
|
|
24
|
+
maxY: number // Right Bottom Y
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type NodeBBox = {
|
|
28
|
+
x: number
|
|
29
|
+
y: number
|
|
30
|
+
width: number
|
|
31
|
+
height: number
|
|
32
|
+
centerX: number
|
|
33
|
+
centerY: number
|
|
34
|
+
} & BoxBoundsPoint
|
|
35
|
+
|
|
36
|
+
interface BoxBounds extends BoxBoundsPoint {
|
|
37
|
+
x: number
|
|
38
|
+
y: number
|
|
39
|
+
width: number
|
|
40
|
+
height: number
|
|
41
|
+
centerX: number
|
|
42
|
+
centerY: number
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
enum SegmentDirection {
|
|
46
|
+
HORIZONTAL = 'horizontal',
|
|
47
|
+
VERTICAL = 'vertical',
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function processEdges(
|
|
51
|
+
lf: LogicFlow,
|
|
52
|
+
rankdir: string | undefined,
|
|
53
|
+
isDefaultAnchor: boolean | undefined,
|
|
54
|
+
edges: BaseEdgeModel[],
|
|
55
|
+
newNodes: NodeConfig[],
|
|
56
|
+
) {
|
|
57
|
+
const newEdges: EdgeConfig[] = []
|
|
58
|
+
// 处理边的路径和锚点
|
|
59
|
+
edges.forEach((edgeModel) => {
|
|
60
|
+
const lfEdge: EdgeConfig = edgeModel.getData()
|
|
61
|
+
if (!lfEdge) {
|
|
62
|
+
throw new Error(`布局错误:找不到ID为 ${edgeModel.id} 的边`)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!isDefaultAnchor) {
|
|
66
|
+
// 自定义锚点,不调整边的关联锚点,只清除路径相关数据,让LogicFlow自动计算
|
|
67
|
+
delete lfEdge.pointsList
|
|
68
|
+
delete lfEdge.startPoint
|
|
69
|
+
delete lfEdge.endPoint
|
|
70
|
+
if (typeof lfEdge.text === 'object' && lfEdge.text && lfEdge.text.value) {
|
|
71
|
+
lfEdge.text = lfEdge.text.value
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
// 默认锚点,重新计算路径以及边的起点和终点(节点默认锚点为上下左右)
|
|
75
|
+
delete lfEdge.pointsList
|
|
76
|
+
delete lfEdge.startPoint
|
|
77
|
+
delete lfEdge.endPoint
|
|
78
|
+
delete lfEdge.sourceAnchorId
|
|
79
|
+
delete lfEdge.targetAnchorId
|
|
80
|
+
|
|
81
|
+
lfEdge.pointsList = calcPointsList(lf, rankdir, edgeModel, newNodes)
|
|
82
|
+
|
|
83
|
+
if (lfEdge.pointsList) {
|
|
84
|
+
// 设置边的起点和终点
|
|
85
|
+
const first = lfEdge.pointsList[0]
|
|
86
|
+
const last = lfEdge.pointsList[lfEdge.pointsList.length - 1]
|
|
87
|
+
lfEdge.startPoint = { x: first.x, y: first.y }
|
|
88
|
+
lfEdge.endPoint = { x: last.x, y: last.y }
|
|
89
|
+
}
|
|
90
|
+
if (typeof lfEdge.text === 'object' && lfEdge.text && lfEdge.text.value) {
|
|
91
|
+
// 保留文本内容
|
|
92
|
+
lfEdge.text = lfEdge.text.value
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
newEdges.push(lfEdge)
|
|
97
|
+
})
|
|
98
|
+
return newEdges
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 优化折线路径点,移除冗余点
|
|
103
|
+
* @param points - 原始路径点数组
|
|
104
|
+
* @returns 优化后的路径点数组
|
|
105
|
+
*/
|
|
106
|
+
function pointFilter(points: Point[]): Point[] {
|
|
107
|
+
const allPoints = [...points] // 创建副本避免修改原始数据
|
|
108
|
+
let i = 1
|
|
109
|
+
|
|
110
|
+
// 删除直线上的中间点(保持路径简洁)
|
|
111
|
+
while (i < allPoints.length - 1) {
|
|
112
|
+
const pre = allPoints[i - 1]
|
|
113
|
+
const current = allPoints[i]
|
|
114
|
+
const next = allPoints[i + 1]
|
|
115
|
+
|
|
116
|
+
// 如果三点共线,移除中间点
|
|
117
|
+
if (
|
|
118
|
+
(pre.x === current.x && current.x === next.x) || // 垂直线上的点
|
|
119
|
+
(pre.y === current.y && current.y === next.y)
|
|
120
|
+
) {
|
|
121
|
+
// 水平线上的点
|
|
122
|
+
allPoints.splice(i, 1)
|
|
123
|
+
} else {
|
|
124
|
+
i++
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return allPoints
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 计算边的折线路径点
|
|
133
|
+
* @param model - 边模型
|
|
134
|
+
* @param nodes - 节点数据数组
|
|
135
|
+
* @returns 计算后的路径点数组,如果无法计算则返回undefined
|
|
136
|
+
*/
|
|
137
|
+
function calcPointsList(
|
|
138
|
+
lf: LogicFlow,
|
|
139
|
+
rankdir: string | undefined,
|
|
140
|
+
model: BaseEdgeModel,
|
|
141
|
+
nodes: NodeConfig[],
|
|
142
|
+
): Point[] | undefined {
|
|
143
|
+
const pointsList: Point[] = []
|
|
144
|
+
|
|
145
|
+
// 获取源节点和目标节点的模型与布局数据
|
|
146
|
+
const sourceNodeModel = lf.getNodeModelById(model.sourceNodeId)
|
|
147
|
+
const targetNodeModel = lf.getNodeModelById(model.targetNodeId)
|
|
148
|
+
const newSourceNodeData = nodes.find(
|
|
149
|
+
(node: NodeConfig) => node.id === model.sourceNodeId,
|
|
150
|
+
)
|
|
151
|
+
const newTargetNodeData = nodes.find(
|
|
152
|
+
(node: NodeConfig) => node.id === model.targetNodeId,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
// 数据验证
|
|
156
|
+
if (
|
|
157
|
+
!sourceNodeModel ||
|
|
158
|
+
!targetNodeModel ||
|
|
159
|
+
!newSourceNodeData ||
|
|
160
|
+
!newTargetNodeData
|
|
161
|
+
) {
|
|
162
|
+
return undefined
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// 折线偏移量(用于创建合适的转折点)
|
|
166
|
+
const offset = Number(model.offset) || 50
|
|
167
|
+
|
|
168
|
+
// 处理从左到右(LR)布局的边路径,折线
|
|
169
|
+
if (rankdir === 'LR' && model.modelType === 'polyline-edge') {
|
|
170
|
+
// 正向连线:源节点在目标节点左侧
|
|
171
|
+
if (newSourceNodeData.x <= newTargetNodeData.x) {
|
|
172
|
+
// 从源节点右侧中心出发
|
|
173
|
+
pointsList.push({
|
|
174
|
+
x: newSourceNodeData.x + sourceNodeModel.width / 2,
|
|
175
|
+
y: newSourceNodeData.y,
|
|
176
|
+
})
|
|
177
|
+
// 向右延伸一段距离
|
|
178
|
+
pointsList.push({
|
|
179
|
+
x: newSourceNodeData.x + sourceNodeModel.width / 2 + offset,
|
|
180
|
+
y: newSourceNodeData.y,
|
|
181
|
+
})
|
|
182
|
+
// 垂直移动到目标节点的高度
|
|
183
|
+
pointsList.push({
|
|
184
|
+
x: newSourceNodeData.x + sourceNodeModel.width / 2 + offset,
|
|
185
|
+
y: newTargetNodeData.y,
|
|
186
|
+
})
|
|
187
|
+
// 连接到目标节点左侧中心
|
|
188
|
+
pointsList.push({
|
|
189
|
+
x: newTargetNodeData.x - targetNodeModel.width / 2,
|
|
190
|
+
y: newTargetNodeData.y,
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
return pointFilter(pointsList)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// 反向连线:源节点在目标节点右侧
|
|
197
|
+
if (newSourceNodeData.x > newTargetNodeData.x) {
|
|
198
|
+
// 根据节点相对Y轴位置选择不同路径
|
|
199
|
+
if (newSourceNodeData.y >= newTargetNodeData.y) {
|
|
200
|
+
// 源节点在目标节点的右下方,从源节点上方出发
|
|
201
|
+
pointsList.push({
|
|
202
|
+
x: newSourceNodeData.x,
|
|
203
|
+
y: newSourceNodeData.y + sourceNodeModel.height / 2,
|
|
204
|
+
})
|
|
205
|
+
pointsList.push({
|
|
206
|
+
x: newSourceNodeData.x,
|
|
207
|
+
y: newSourceNodeData.y + sourceNodeModel.height / 2 + offset,
|
|
208
|
+
})
|
|
209
|
+
pointsList.push({
|
|
210
|
+
x: newTargetNodeData.x,
|
|
211
|
+
y: newSourceNodeData.y + sourceNodeModel.height / 2 + offset,
|
|
212
|
+
})
|
|
213
|
+
pointsList.push({
|
|
214
|
+
x: newTargetNodeData.x,
|
|
215
|
+
y: newTargetNodeData.y + targetNodeModel.height / 2,
|
|
216
|
+
})
|
|
217
|
+
} else {
|
|
218
|
+
// 源节点在目标节点的右上方,从源节点下方出发
|
|
219
|
+
pointsList.push({
|
|
220
|
+
x: newSourceNodeData.x,
|
|
221
|
+
y: newSourceNodeData.y - sourceNodeModel.height / 2,
|
|
222
|
+
})
|
|
223
|
+
pointsList.push({
|
|
224
|
+
x: newSourceNodeData.x,
|
|
225
|
+
y: newSourceNodeData.y - sourceNodeModel.height / 2 - offset,
|
|
226
|
+
})
|
|
227
|
+
pointsList.push({
|
|
228
|
+
x: newTargetNodeData.x,
|
|
229
|
+
y: newSourceNodeData.y - sourceNodeModel.height / 2 - offset,
|
|
230
|
+
})
|
|
231
|
+
pointsList.push({
|
|
232
|
+
x: newTargetNodeData.x,
|
|
233
|
+
y: newTargetNodeData.y - targetNodeModel.height / 2,
|
|
234
|
+
})
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return pointFilter(pointsList)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// 处理从上到下(TB)布局的边路径, 折线
|
|
242
|
+
if (rankdir === 'TB' && model.modelType === 'polyline-edge') {
|
|
243
|
+
// 正向连线:源节点在目标节点上方
|
|
244
|
+
if (newSourceNodeData.y <= newTargetNodeData.y) {
|
|
245
|
+
// 从源节点底部中心出发
|
|
246
|
+
pointsList.push({
|
|
247
|
+
x: newSourceNodeData.x,
|
|
248
|
+
y: newSourceNodeData.y + sourceNodeModel.height / 2,
|
|
249
|
+
})
|
|
250
|
+
// 向下延伸一段距离
|
|
251
|
+
pointsList.push({
|
|
252
|
+
x: newSourceNodeData.x,
|
|
253
|
+
y: newSourceNodeData.y + sourceNodeModel.height / 2 + offset,
|
|
254
|
+
})
|
|
255
|
+
// 水平移动到目标节点的位置
|
|
256
|
+
pointsList.push({
|
|
257
|
+
x: newTargetNodeData.x,
|
|
258
|
+
y: newSourceNodeData.y + sourceNodeModel.height / 2 + offset,
|
|
259
|
+
})
|
|
260
|
+
// 连接到目标节点顶部中心
|
|
261
|
+
pointsList.push({
|
|
262
|
+
x: newTargetNodeData.x,
|
|
263
|
+
y: newTargetNodeData.y - targetNodeModel.height / 2,
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
return pointFilter(pointsList)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// 反向连线:源节点在目标节点下方
|
|
270
|
+
if (newSourceNodeData.y > newTargetNodeData.y) {
|
|
271
|
+
if (newSourceNodeData.x >= newTargetNodeData.x) {
|
|
272
|
+
// 源节点在目标节点右下方,从源节点右侧出发
|
|
273
|
+
pointsList.push({
|
|
274
|
+
x: newSourceNodeData.x + sourceNodeModel.width / 2,
|
|
275
|
+
y: newSourceNodeData.y,
|
|
276
|
+
})
|
|
277
|
+
pointsList.push({
|
|
278
|
+
x: newSourceNodeData.x + sourceNodeModel.width / 2 + offset,
|
|
279
|
+
y: newSourceNodeData.y,
|
|
280
|
+
})
|
|
281
|
+
pointsList.push({
|
|
282
|
+
x: newSourceNodeData.x + sourceNodeModel.width / 2 + offset,
|
|
283
|
+
y: newTargetNodeData.y,
|
|
284
|
+
})
|
|
285
|
+
pointsList.push({
|
|
286
|
+
x: newTargetNodeData.x + targetNodeModel.width / 2,
|
|
287
|
+
y: newTargetNodeData.y,
|
|
288
|
+
})
|
|
289
|
+
} else {
|
|
290
|
+
// 源节点在目标节点左下方,从源节点左侧出发
|
|
291
|
+
pointsList.push({
|
|
292
|
+
x: newSourceNodeData.x - sourceNodeModel.width / 2,
|
|
293
|
+
y: newSourceNodeData.y,
|
|
294
|
+
})
|
|
295
|
+
pointsList.push({
|
|
296
|
+
x: newSourceNodeData.x - sourceNodeModel.width / 2 - offset,
|
|
297
|
+
y: newSourceNodeData.y,
|
|
298
|
+
})
|
|
299
|
+
pointsList.push({
|
|
300
|
+
x: newSourceNodeData.x - sourceNodeModel.width / 2 - offset,
|
|
301
|
+
y: newTargetNodeData.y,
|
|
302
|
+
})
|
|
303
|
+
pointsList.push({
|
|
304
|
+
x: newTargetNodeData.x - targetNodeModel.width / 2,
|
|
305
|
+
y: newTargetNodeData.y,
|
|
306
|
+
})
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return pointFilter(pointsList)
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// 处理从左到右(LR)布局的边路径, 贝塞尔曲线
|
|
314
|
+
if (rankdir === 'LR' && model.modelType === 'bezier-edge') {
|
|
315
|
+
const startPoint = {
|
|
316
|
+
x: newSourceNodeData.x + sourceNodeModel.width / 2,
|
|
317
|
+
y: newSourceNodeData.y,
|
|
318
|
+
}
|
|
319
|
+
const endPoint = {
|
|
320
|
+
x: newTargetNodeData.x - targetNodeModel.width / 2,
|
|
321
|
+
y: newTargetNodeData.y,
|
|
322
|
+
}
|
|
323
|
+
let sNext: Point = { x: 0, y: 0 }
|
|
324
|
+
let ePre: Point = { x: 0, y: 0 }
|
|
325
|
+
// 正向连线:源节点在目标节点左侧
|
|
326
|
+
if (newSourceNodeData.x < newTargetNodeData.x) {
|
|
327
|
+
const result = getBezierControlPoints({
|
|
328
|
+
start: startPoint,
|
|
329
|
+
end: endPoint,
|
|
330
|
+
sourceNode: {
|
|
331
|
+
x: newSourceNodeData.x,
|
|
332
|
+
y: newSourceNodeData.y,
|
|
333
|
+
width: sourceNodeModel.width,
|
|
334
|
+
height: sourceNodeModel.height,
|
|
335
|
+
},
|
|
336
|
+
targetNode: {
|
|
337
|
+
x: newTargetNodeData.x,
|
|
338
|
+
y: newTargetNodeData.y,
|
|
339
|
+
width: targetNodeModel.width,
|
|
340
|
+
height: targetNodeModel.height,
|
|
341
|
+
},
|
|
342
|
+
offset,
|
|
343
|
+
})
|
|
344
|
+
sNext = result.sNext
|
|
345
|
+
ePre = result.ePre
|
|
346
|
+
}
|
|
347
|
+
// 反向连线:源节点在目标节点右侧
|
|
348
|
+
if (newSourceNodeData.x > newTargetNodeData.x) {
|
|
349
|
+
if (newSourceNodeData.y >= newTargetNodeData.y) {
|
|
350
|
+
// 源节点在目标节点的右下方
|
|
351
|
+
sNext = {
|
|
352
|
+
x:
|
|
353
|
+
newSourceNodeData.x +
|
|
354
|
+
sourceNodeModel.width / 2 +
|
|
355
|
+
offset +
|
|
356
|
+
targetNodeModel.width / 2,
|
|
357
|
+
y:
|
|
358
|
+
newSourceNodeData.y +
|
|
359
|
+
sourceNodeModel.height +
|
|
360
|
+
targetNodeModel.height,
|
|
361
|
+
}
|
|
362
|
+
ePre = {
|
|
363
|
+
x:
|
|
364
|
+
newTargetNodeData.x -
|
|
365
|
+
targetNodeModel.width / 2 -
|
|
366
|
+
offset -
|
|
367
|
+
sourceNodeModel.width / 2,
|
|
368
|
+
y:
|
|
369
|
+
newTargetNodeData.y +
|
|
370
|
+
sourceNodeModel.height +
|
|
371
|
+
targetNodeModel.height,
|
|
372
|
+
}
|
|
373
|
+
} else {
|
|
374
|
+
// 源节点在目标节点的右上方
|
|
375
|
+
sNext = {
|
|
376
|
+
x:
|
|
377
|
+
newSourceNodeData.x +
|
|
378
|
+
sourceNodeModel.width / 2 +
|
|
379
|
+
offset +
|
|
380
|
+
targetNodeModel.width / 2,
|
|
381
|
+
y:
|
|
382
|
+
newSourceNodeData.y -
|
|
383
|
+
sourceNodeModel.height -
|
|
384
|
+
targetNodeModel.height,
|
|
385
|
+
}
|
|
386
|
+
ePre = {
|
|
387
|
+
x:
|
|
388
|
+
newTargetNodeData.x -
|
|
389
|
+
targetNodeModel.width / 2 -
|
|
390
|
+
offset -
|
|
391
|
+
sourceNodeModel.width / 2,
|
|
392
|
+
y:
|
|
393
|
+
newTargetNodeData.y -
|
|
394
|
+
sourceNodeModel.height -
|
|
395
|
+
targetNodeModel.height,
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
pointsList.push(startPoint, sNext, ePre, endPoint)
|
|
400
|
+
return pointsList
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// 处理从上到下(TB)布局的边路径, 贝塞尔曲线
|
|
404
|
+
if (rankdir === 'TB' && model.modelType === 'bezier-edge') {
|
|
405
|
+
const startPoint = {
|
|
406
|
+
x: newSourceNodeData.x,
|
|
407
|
+
y: newSourceNodeData.y + sourceNodeModel.height / 2,
|
|
408
|
+
}
|
|
409
|
+
const endPoint = {
|
|
410
|
+
x: newTargetNodeData.x,
|
|
411
|
+
y: newTargetNodeData.y - targetNodeModel.height / 2,
|
|
412
|
+
}
|
|
413
|
+
let sNext: Point = { x: 0, y: 0 }
|
|
414
|
+
let ePre: Point = { x: 0, y: 0 }
|
|
415
|
+
if (newSourceNodeData.y <= newTargetNodeData.y) {
|
|
416
|
+
// 正向连线:源节点在目标节点上方
|
|
417
|
+
const result = getBezierControlPoints({
|
|
418
|
+
start: startPoint,
|
|
419
|
+
end: endPoint,
|
|
420
|
+
sourceNode: {
|
|
421
|
+
x: newSourceNodeData.x,
|
|
422
|
+
y: newSourceNodeData.y,
|
|
423
|
+
width: sourceNodeModel.width,
|
|
424
|
+
height: sourceNodeModel.height,
|
|
425
|
+
},
|
|
426
|
+
targetNode: {
|
|
427
|
+
x: newTargetNodeData.x,
|
|
428
|
+
y: newTargetNodeData.y,
|
|
429
|
+
width: targetNodeModel.width,
|
|
430
|
+
height: targetNodeModel.height,
|
|
431
|
+
},
|
|
432
|
+
offset,
|
|
433
|
+
})
|
|
434
|
+
sNext = result.sNext
|
|
435
|
+
ePre = result.ePre
|
|
436
|
+
}
|
|
437
|
+
if (newSourceNodeData.y > newTargetNodeData.y) {
|
|
438
|
+
// 反向连线:源节点在目标节点下方
|
|
439
|
+
if (newSourceNodeData.x >= newTargetNodeData.x) {
|
|
440
|
+
// 源节点在目标节点右下方
|
|
441
|
+
sNext = {
|
|
442
|
+
x:
|
|
443
|
+
newSourceNodeData.x +
|
|
444
|
+
sourceNodeModel.width / 2 +
|
|
445
|
+
offset +
|
|
446
|
+
targetNodeModel.width / 2,
|
|
447
|
+
y:
|
|
448
|
+
newSourceNodeData.y +
|
|
449
|
+
sourceNodeModel.height +
|
|
450
|
+
targetNodeModel.height,
|
|
451
|
+
}
|
|
452
|
+
ePre = {
|
|
453
|
+
x:
|
|
454
|
+
newTargetNodeData.x +
|
|
455
|
+
targetNodeModel.width / 2 +
|
|
456
|
+
offset +
|
|
457
|
+
sourceNodeModel.width / 2,
|
|
458
|
+
y:
|
|
459
|
+
newTargetNodeData.y -
|
|
460
|
+
sourceNodeModel.height -
|
|
461
|
+
targetNodeModel.height,
|
|
462
|
+
}
|
|
463
|
+
} else {
|
|
464
|
+
// 源节点在目标节点左下方
|
|
465
|
+
sNext = {
|
|
466
|
+
x:
|
|
467
|
+
newSourceNodeData.x -
|
|
468
|
+
sourceNodeModel.width / 2 -
|
|
469
|
+
offset -
|
|
470
|
+
targetNodeModel.width / 2,
|
|
471
|
+
y:
|
|
472
|
+
newSourceNodeData.y +
|
|
473
|
+
sourceNodeModel.height +
|
|
474
|
+
targetNodeModel.height,
|
|
475
|
+
}
|
|
476
|
+
ePre = {
|
|
477
|
+
x:
|
|
478
|
+
newTargetNodeData.x -
|
|
479
|
+
targetNodeModel.width / 2 -
|
|
480
|
+
offset -
|
|
481
|
+
sourceNodeModel.width / 2,
|
|
482
|
+
y:
|
|
483
|
+
newTargetNodeData.y -
|
|
484
|
+
sourceNodeModel.height -
|
|
485
|
+
targetNodeModel.height,
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
pointsList.push(startPoint, sNext, ePre, endPoint)
|
|
490
|
+
return pointsList
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// 无法确定路径时返回undefined,让LogicFlow自行处理
|
|
494
|
+
return undefined
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// bezier曲线
|
|
498
|
+
const getBezierControlPoints = ({
|
|
499
|
+
start,
|
|
500
|
+
end,
|
|
501
|
+
sourceNode,
|
|
502
|
+
targetNode,
|
|
503
|
+
offset,
|
|
504
|
+
}: {
|
|
505
|
+
start: Point
|
|
506
|
+
end: Point
|
|
507
|
+
sourceNode: BaseNodeData
|
|
508
|
+
targetNode: BaseNodeData
|
|
509
|
+
offset: number
|
|
510
|
+
}): IBezierControls => {
|
|
511
|
+
const sBBox = getNodeBBox(sourceNode)
|
|
512
|
+
const tBBox = getNodeBBox(targetNode)
|
|
513
|
+
const sExpendBBox = getExpandedBBox(sBBox, offset)
|
|
514
|
+
const tExpendBBox = getExpandedBBox(tBBox, offset)
|
|
515
|
+
const sNext = getExpandedBBoxPoint(sExpendBBox, sBBox, start)
|
|
516
|
+
const ePre = getExpandedBBoxPoint(tExpendBBox, tBBox, end)
|
|
517
|
+
return {
|
|
518
|
+
sNext,
|
|
519
|
+
ePre,
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/* 获取节点bbox */
|
|
524
|
+
const getNodeBBox = (node: BaseNodeData): NodeBBox => {
|
|
525
|
+
const { x, y, width, height } = node
|
|
526
|
+
return {
|
|
527
|
+
minX: x - width / 2,
|
|
528
|
+
minY: y - height / 2,
|
|
529
|
+
maxX: x + width / 2,
|
|
530
|
+
maxY: y + height / 2,
|
|
531
|
+
x,
|
|
532
|
+
y,
|
|
533
|
+
width,
|
|
534
|
+
height,
|
|
535
|
+
centerX: x,
|
|
536
|
+
centerY: y,
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/* 扩展的bbox,保证起始点的下一个点一定在node的垂直方向,不会与线重合, offset是点与线的垂直距离 */
|
|
541
|
+
const getExpandedBBox = (bbox: BoxBounds, offset: number): BoxBounds => {
|
|
542
|
+
if (bbox.width === 0 && bbox.height === 0) {
|
|
543
|
+
return bbox
|
|
544
|
+
}
|
|
545
|
+
return {
|
|
546
|
+
x: bbox.x,
|
|
547
|
+
y: bbox.y,
|
|
548
|
+
centerX: bbox.centerX,
|
|
549
|
+
centerY: bbox.centerY,
|
|
550
|
+
minX: bbox.minX - offset,
|
|
551
|
+
minY: bbox.minY - offset,
|
|
552
|
+
maxX: bbox.maxX + offset,
|
|
553
|
+
maxY: bbox.maxY + offset,
|
|
554
|
+
height: bbox.height + 2 * offset,
|
|
555
|
+
width: bbox.width + 2 * offset,
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/* 判断点与中心点边的方向:是否水平,true水平,false垂直 */
|
|
560
|
+
const pointDirection = (point: Point, bbox: BoxBounds): Direction => {
|
|
561
|
+
const dx = Math.abs(point.x - bbox.centerX)
|
|
562
|
+
const dy = Math.abs(point.y - bbox.centerY)
|
|
563
|
+
return dx / bbox.width > dy / bbox.height
|
|
564
|
+
? SegmentDirection.HORIZONTAL
|
|
565
|
+
: SegmentDirection.VERTICAL
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/* 获取扩展图形上的点,即起始终点相邻的点,上一个或者下一个节点 */
|
|
569
|
+
const getExpandedBBoxPoint = (
|
|
570
|
+
expendBBox: BoxBounds,
|
|
571
|
+
bbox: BoxBounds,
|
|
572
|
+
point: Point,
|
|
573
|
+
): Point => {
|
|
574
|
+
const direction = pointDirection(point, bbox)
|
|
575
|
+
if (direction === SegmentDirection.HORIZONTAL) {
|
|
576
|
+
return {
|
|
577
|
+
x: point.x > expendBBox.centerX ? expendBBox.maxX : expendBBox.minX,
|
|
578
|
+
y: point.y,
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return {
|
|
582
|
+
x: point.x,
|
|
583
|
+
y: point.y > expendBBox.centerY ? expendBBox.maxY : expendBBox.minY,
|
|
584
|
+
}
|
|
585
|
+
}
|