-
Notifications
You must be signed in to change notification settings - Fork 373
Expand file tree
/
Copy pathdagreLayout.ts
More file actions
101 lines (90 loc) · 2.4 KB
/
dagreLayout.ts
File metadata and controls
101 lines (90 loc) · 2.4 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
import {
DEFAULT_NODE_WIDTH,
type EdgeId,
type LineageEdge,
type LineageEdgeData,
type LineageNodeData,
type LineageNodesMap,
type NodeId,
type PortId,
} from '../utils'
import dagre from 'dagre'
export function buildLayout<
TNodeData extends LineageNodeData = LineageNodeData,
TEdgeData extends LineageEdgeData = LineageEdgeData,
TEdgeID extends string = EdgeId,
TSourceID extends string = NodeId,
TTargetID extends string = NodeId,
TSourceHandleID extends string = PortId,
TTargetHandleID extends string = PortId,
>({
edges,
nodesMap,
shouldReuseExistingPosition = true,
}: {
edges: LineageEdge<
TEdgeData,
TEdgeID,
TSourceID,
TTargetID,
TSourceHandleID,
TTargetHandleID
>[]
nodesMap: LineageNodesMap<TNodeData>
shouldReuseExistingPosition?: boolean
}) {
const nodes = Object.values(nodesMap)
const nodeCount = nodes.length
const edgeCount = edges.length
if (nodeCount === 0) return {}
const g = new dagre.graphlib.Graph({
compound: true,
multigraph: true,
directed: true,
})
g.setGraph({
rankdir: 'LR',
nodesep: 12,
ranksep: 48,
edgesep: 0,
ranker: 'longest-path',
})
g.setDefaultEdgeLabel(() => ({}))
// Building layout already heavy operation, so trying to optimize it a bit
for (let i = 0; i < edgeCount; i++) {
g.setEdge(edges[i].source, edges[i].target)
}
for (let i = 0; i < nodeCount; i++) {
const node = nodes[i]
g.setNode(node.id, {
width: node.width || DEFAULT_NODE_WIDTH,
height: node.height || 0,
})
}
dagre.layout(g)
// Building layout already heavy operation, so trying to optimize it a bit
for (let i = 0; i < nodeCount; i++) {
const node = nodes[i]
const width = node.width || DEFAULT_NODE_WIDTH
const height = node.height || 0
const nodeId = node.id as NodeId
const nodeWithPosition = g.node(nodeId)
const halfWidth = width / 2
const halfHeight = height / 2
const isDefaultPosition = node.position.x === 0 && node.position.y === 0
nodesMap[nodeId] = {
...node,
position: {
x:
shouldReuseExistingPosition && isDefaultPosition
? nodeWithPosition.x - halfWidth
: node.position.x,
y:
shouldReuseExistingPosition && isDefaultPosition
? nodeWithPosition.y - halfHeight
: node.position.y,
},
}
}
return { ...nodesMap }
}