You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

layout.ts 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. import dagre from '@dagrejs/dagre'
  2. import {
  3. cloneDeep,
  4. } from 'lodash-es'
  5. import type {
  6. Edge,
  7. Node,
  8. } from '../types'
  9. import {
  10. BlockEnum,
  11. } from '../types'
  12. import {
  13. CUSTOM_NODE,
  14. NODE_LAYOUT_HORIZONTAL_PADDING,
  15. NODE_LAYOUT_MIN_DISTANCE,
  16. NODE_LAYOUT_VERTICAL_PADDING,
  17. } from '../constants'
  18. import { CUSTOM_ITERATION_START_NODE } from '../nodes/iteration-start/constants'
  19. import { CUSTOM_LOOP_START_NODE } from '../nodes/loop-start/constants'
  20. export const getLayoutByDagre = (originNodes: Node[], originEdges: Edge[]) => {
  21. const dagreGraph = new dagre.graphlib.Graph()
  22. dagreGraph.setDefaultEdgeLabel(() => ({}))
  23. const nodes = cloneDeep(originNodes).filter(node => !node.parentId && node.type === CUSTOM_NODE)
  24. const edges = cloneDeep(originEdges).filter(edge => (!edge.data?.isInIteration && !edge.data?.isInLoop))
  25. dagreGraph.setGraph({
  26. rankdir: 'LR',
  27. align: 'UL',
  28. nodesep: 40,
  29. ranksep: 60,
  30. ranker: 'tight-tree',
  31. marginx: 30,
  32. marginy: 200,
  33. })
  34. nodes.forEach((node) => {
  35. dagreGraph.setNode(node.id, {
  36. width: node.width!,
  37. height: node.height!,
  38. })
  39. })
  40. edges.forEach((edge) => {
  41. dagreGraph.setEdge(edge.source, edge.target)
  42. })
  43. dagre.layout(dagreGraph)
  44. return dagreGraph
  45. }
  46. export const getLayoutForChildNodes = (parentNodeId: string, originNodes: Node[], originEdges: Edge[]) => {
  47. const dagreGraph = new dagre.graphlib.Graph()
  48. dagreGraph.setDefaultEdgeLabel(() => ({}))
  49. const nodes = cloneDeep(originNodes).filter(node => node.parentId === parentNodeId)
  50. const edges = cloneDeep(originEdges).filter(edge =>
  51. (edge.data?.isInIteration && edge.data?.iteration_id === parentNodeId)
  52. || (edge.data?.isInLoop && edge.data?.loop_id === parentNodeId),
  53. )
  54. const startNode = nodes.find(node =>
  55. node.type === CUSTOM_ITERATION_START_NODE
  56. || node.type === CUSTOM_LOOP_START_NODE
  57. || node.data?.type === BlockEnum.LoopStart
  58. || node.data?.type === BlockEnum.IterationStart,
  59. )
  60. if (!startNode) {
  61. dagreGraph.setGraph({
  62. rankdir: 'LR',
  63. align: 'UL',
  64. nodesep: 40,
  65. ranksep: 60,
  66. marginx: NODE_LAYOUT_HORIZONTAL_PADDING,
  67. marginy: NODE_LAYOUT_VERTICAL_PADDING,
  68. })
  69. nodes.forEach((node) => {
  70. dagreGraph.setNode(node.id, {
  71. width: node.width || 244,
  72. height: node.height || 100,
  73. })
  74. })
  75. edges.forEach((edge) => {
  76. dagreGraph.setEdge(edge.source, edge.target)
  77. })
  78. dagre.layout(dagreGraph)
  79. return dagreGraph
  80. }
  81. const startNodeOutEdges = edges.filter(edge => edge.source === startNode.id)
  82. const firstConnectedNodes = startNodeOutEdges.map(edge =>
  83. nodes.find(node => node.id === edge.target),
  84. ).filter(Boolean) as Node[]
  85. const nonStartNodes = nodes.filter(node => node.id !== startNode.id)
  86. const nonStartEdges = edges.filter(edge => edge.source !== startNode.id && edge.target !== startNode.id)
  87. dagreGraph.setGraph({
  88. rankdir: 'LR',
  89. align: 'UL',
  90. nodesep: 40,
  91. ranksep: 60,
  92. marginx: NODE_LAYOUT_HORIZONTAL_PADDING / 2,
  93. marginy: NODE_LAYOUT_VERTICAL_PADDING / 2,
  94. })
  95. nonStartNodes.forEach((node) => {
  96. dagreGraph.setNode(node.id, {
  97. width: node.width || 244,
  98. height: node.height || 100,
  99. })
  100. })
  101. nonStartEdges.forEach((edge) => {
  102. dagreGraph.setEdge(edge.source, edge.target)
  103. })
  104. dagre.layout(dagreGraph)
  105. const startNodeSize = {
  106. width: startNode.width || 44,
  107. height: startNode.height || 48,
  108. }
  109. const startNodeX = NODE_LAYOUT_HORIZONTAL_PADDING / 1.5
  110. let startNodeY = 100
  111. let minFirstLayerX = Infinity
  112. let avgFirstLayerY = 0
  113. let firstLayerCount = 0
  114. if (firstConnectedNodes.length > 0) {
  115. firstConnectedNodes.forEach((node) => {
  116. if (dagreGraph.node(node.id)) {
  117. const nodePos = dagreGraph.node(node.id)
  118. avgFirstLayerY += nodePos.y
  119. firstLayerCount++
  120. minFirstLayerX = Math.min(minFirstLayerX, nodePos.x - nodePos.width / 2)
  121. }
  122. })
  123. if (firstLayerCount > 0) {
  124. avgFirstLayerY /= firstLayerCount
  125. startNodeY = avgFirstLayerY
  126. }
  127. const minRequiredX = startNodeX + startNodeSize.width + NODE_LAYOUT_MIN_DISTANCE
  128. if (minFirstLayerX < minRequiredX) {
  129. const shiftX = minRequiredX - minFirstLayerX
  130. nonStartNodes.forEach((node) => {
  131. if (dagreGraph.node(node.id)) {
  132. const nodePos = dagreGraph.node(node.id)
  133. dagreGraph.setNode(node.id, {
  134. x: nodePos.x + shiftX,
  135. y: nodePos.y,
  136. width: nodePos.width,
  137. height: nodePos.height,
  138. })
  139. }
  140. })
  141. }
  142. }
  143. dagreGraph.setNode(startNode.id, {
  144. x: startNodeX + startNodeSize.width / 2,
  145. y: startNodeY,
  146. width: startNodeSize.width,
  147. height: startNodeSize.height,
  148. })
  149. startNodeOutEdges.forEach((edge) => {
  150. dagreGraph.setEdge(edge.source, edge.target)
  151. })
  152. return dagreGraph
  153. }