UNPKG

@jalez/react-flow-smart-edge

Version:

Smart edge routing for @xyflow/react v12+ (maintained fork of @tisoap/react-flow-smart-edge)

144 lines (125 loc) 3.19 kB
import { roundUp, roundDown } from './utils' import type { Node, XYPosition } from '@xyflow/react' export type NodeBoundingBox = { id: string width: number height: number topLeft: XYPosition bottomLeft: XYPosition topRight: XYPosition bottomRight: XYPosition } export type GraphBoundingBox = { width: number height: number topLeft: XYPosition bottomLeft: XYPosition topRight: XYPosition bottomRight: XYPosition xMax: number yMax: number xMin: number yMin: number } /** * Get the bounding box of all nodes and the graph itself, as X/Y coordinates * of all corner points. * * @param nodes The node list * @param nodePadding Optional padding to add to the node's and graph bounding boxes * @param roundTo Everything will be rounded to this nearest integer * @returns Graph and nodes bounding boxes. */ export const getBoundingBoxes = ( nodes: Node[], nodePadding = 2, roundTo = 2 ) => { let xMax = Number.MIN_SAFE_INTEGER let yMax = Number.MIN_SAFE_INTEGER let xMin = Number.MAX_SAFE_INTEGER let yMin = Number.MAX_SAFE_INTEGER const nodeBoxes: NodeBoundingBox[] = nodes.map((node) => { const width = Math.max(node.width || 0, 1) const height = Math.max(node.height || 0, 1) const position: XYPosition = { x: node.position.x || 0, y: node.position.y || 0 } const topLeft: XYPosition = { x: position.x - nodePadding, y: position.y - nodePadding } const bottomLeft: XYPosition = { x: position.x - nodePadding, y: position.y + height + nodePadding } const topRight: XYPosition = { x: position.x + width + nodePadding, y: position.y - nodePadding } const bottomRight: XYPosition = { x: position.x + width + nodePadding, y: position.y + height + nodePadding } if (roundTo > 0) { topLeft.x = roundDown(topLeft.x, roundTo) topLeft.y = roundDown(topLeft.y, roundTo) bottomLeft.x = roundDown(bottomLeft.x, roundTo) bottomLeft.y = roundUp(bottomLeft.y, roundTo) topRight.x = roundUp(topRight.x, roundTo) topRight.y = roundDown(topRight.y, roundTo) bottomRight.x = roundUp(bottomRight.x, roundTo) bottomRight.y = roundUp(bottomRight.y, roundTo) } if (topLeft.y < yMin) yMin = topLeft.y if (topLeft.x < xMin) xMin = topLeft.x if (bottomRight.y > yMax) yMax = bottomRight.y if (bottomRight.x > xMax) xMax = bottomRight.x return { id: node.id, width, height, topLeft, bottomLeft, topRight, bottomRight } }) const graphPadding = nodePadding * 2 xMax = roundUp(xMax + graphPadding, roundTo) yMax = roundUp(yMax + graphPadding, roundTo) xMin = roundDown(xMin - graphPadding, roundTo) yMin = roundDown(yMin - graphPadding, roundTo) const topLeft: XYPosition = { x: xMin, y: yMin } const bottomLeft: XYPosition = { x: xMin, y: yMax } const topRight: XYPosition = { x: xMax, y: yMin } const bottomRight: XYPosition = { x: xMax, y: yMax } const width = Math.abs(topLeft.x - topRight.x) const height = Math.abs(topLeft.y - bottomLeft.y) const graphBox: GraphBoundingBox = { topLeft, bottomLeft, topRight, bottomRight, width, height, xMax, yMax, xMin, yMin } return { nodeBoxes, graphBox } }