-
Notifications
You must be signed in to change notification settings - Fork 30
Expand file tree
/
Copy pathindex.ts
More file actions
127 lines (121 loc) · 3.57 KB
/
index.ts
File metadata and controls
127 lines (121 loc) · 3.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import { areLinesSameDirection, isHorizontalFromPosition } from '../edge';
import {
type ControlPoint,
getCenterPoints,
getExpandedRect,
getOffsetPoint,
getSidesFromPoints,
getVerticesFromRectVertex,
type HandlePosition,
type NodeRect,
optimizeInputPoints,
reducePoints,
} from '../point';
import { getAStarPath } from './a-star';
import { getSimplePath } from './simple';
export interface GetControlPointsParams {
source: HandlePosition;
target: HandlePosition;
sourceRect: NodeRect;
targetRect: NodeRect;
/**
* Minimum spacing between edges and nodes
*/
offset: number;
}
/**
* Calculate control points on the optimal path of an edge.
*
* Reference article: https://juejin.cn/post/6942727734518874142
*/
export const getControlPoints = ({
source: oldSource,
target: oldTarget,
sourceRect,
targetRect,
offset = 20,
}: GetControlPointsParams) => {
const source: ControlPoint = oldSource;
const target: ControlPoint = oldTarget;
let edgePoints: ControlPoint[] = [];
let optimized: ReturnType<typeof optimizeInputPoints>;
// 1. Find the starting and ending points after applying the offset
const sourceOffset = getOffsetPoint(oldSource, offset);
const targetOffset = getOffsetPoint(oldTarget, offset);
const expandedSource = getExpandedRect(sourceRect, offset);
const expandedTarget = getExpandedRect(targetRect, offset);
// 2. Determine if the two Rects are relatively close or should directly connected
const minOffset = 2 * offset + 10;
const isHorizontalLayout = isHorizontalFromPosition(oldSource.position);
const isSameDirection = areLinesSameDirection(
source,
sourceOffset,
targetOffset,
target,
);
const sides = getSidesFromPoints([
source,
target,
sourceOffset,
targetOffset,
]);
const isTooClose = isHorizontalLayout
? sides.right - sides.left < minOffset
: sides.bottom - sides.top < minOffset;
const isDirectConnect = isHorizontalLayout
? isSameDirection && source.x < target.x
: isSameDirection && source.y < target.y;
if (isTooClose || isDirectConnect) {
// 3. If the two Rects are relatively close or directly connected, return a simple Path
edgePoints = getSimplePath({
source,
target,
sourceOffset,
targetOffset,
isDirectConnect,
});
optimized = optimizeInputPoints({
source: oldSource,
target: oldTarget,
sourceOffset,
targetOffset,
edgePoints,
});
edgePoints = optimized.edgePoints;
} else {
// 3. Find the vertices of the two expanded Rects
edgePoints = [
...getVerticesFromRectVertex(expandedSource, targetOffset),
...getVerticesFromRectVertex(expandedTarget, sourceOffset),
];
// 4. Find possible midpoints and intersections
edgePoints = edgePoints.concat(
getCenterPoints({
source: expandedSource,
target: expandedTarget,
sourceOffset,
targetOffset,
}),
);
// 5. Merge nearby coordinate points and remove duplicate coordinate points
optimized = optimizeInputPoints({
source: oldSource,
target: oldTarget,
sourceOffset,
targetOffset,
edgePoints,
});
// 6. Find the optimal path
edgePoints = getAStarPath({
points: optimized.edgePoints,
source: optimized.source,
target: optimized.target,
sourceRect: getExpandedRect(sourceRect, offset / 2),
targetRect: getExpandedRect(targetRect, offset / 2),
});
}
return {
points: reducePoints([optimized.source, ...edgePoints, optimized.target]),
inputPoints: optimized.edgePoints,
};
};