|
| 1 | +import { Position, type XYPosition } from "@xyflow/system"; |
| 2 | + |
| 3 | +import type { InternalNode } from "@/types"; |
| 4 | + |
| 5 | +// this helper function returns the intersection point |
| 6 | +// of the line between the center of the intersectionNode and the target node |
| 7 | +function getNodeIntersection(intersectionNode: InternalNode, targetNode: InternalNode) { |
| 8 | + // https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a |
| 9 | + |
| 10 | + const { width: intersectionNodeWidth, height: intersectionNodeHeight } = |
| 11 | + intersectionNode.measured; |
| 12 | + const intersectionNodePosition = intersectionNode.internals.positionAbsolute; |
| 13 | + const targetPosition = targetNode.internals.positionAbsolute!; |
| 14 | + |
| 15 | + const w = intersectionNodeWidth! / 2; |
| 16 | + const h = intersectionNodeHeight! / 2; |
| 17 | + |
| 18 | + const x2 = intersectionNodePosition.x + w; |
| 19 | + const y2 = intersectionNodePosition.y + h; |
| 20 | + const x1 = targetPosition.x + w; |
| 21 | + const y1 = targetPosition.y + h; |
| 22 | + |
| 23 | + const xx1 = (x1 - x2) / (2 * w) - (y1 - y2) / (2 * h); |
| 24 | + const yy1 = (x1 - x2) / (2 * w) + (y1 - y2) / (2 * h); |
| 25 | + const a = 1 / (Math.abs(xx1) + Math.abs(yy1) || 1); |
| 26 | + const xx3 = a * xx1; |
| 27 | + const yy3 = a * yy1; |
| 28 | + const x = w * (xx3 + yy3) + x2; |
| 29 | + const y = h * (-xx3 + yy3) + y2; |
| 30 | + return { x, y }; |
| 31 | +} |
| 32 | + |
| 33 | +// returns the position (top,right,bottom or right) passed node compared to the intersection point |
| 34 | +function getEdgePosition(node: InternalNode, intersectionPoint: XYPosition) { |
| 35 | + const n = { ...node.internals.positionAbsolute, ...node }; |
| 36 | + const nx = Math.round(n.x!); |
| 37 | + const ny = Math.round(n.y!); |
| 38 | + const px = Math.round(intersectionPoint.x); |
| 39 | + const py = Math.round(intersectionPoint.y); |
| 40 | + |
| 41 | + const { width = 0, height = 0 } = node.measured; |
| 42 | + |
| 43 | + if (px <= nx + 1) { |
| 44 | + return Position.Left; |
| 45 | + } |
| 46 | + if (px >= nx + width - 1) { |
| 47 | + return Position.Right; |
| 48 | + } |
| 49 | + if (py <= ny + 1) { |
| 50 | + return Position.Top; |
| 51 | + } |
| 52 | + if (py >= n.y! + height - 1) { |
| 53 | + return Position.Bottom; |
| 54 | + } |
| 55 | + |
| 56 | + return Position.Top; |
| 57 | +} |
| 58 | + |
| 59 | +// returns the parameters (sx, sy, tx, ty, sourcePos, targetPos) you need to create an edge |
| 60 | +export function getEdgeParams(source: InternalNode, target: InternalNode) { |
| 61 | + const sourceIntersectionPoint = getNodeIntersection(source, target); |
| 62 | + const targetIntersectionPoint = getNodeIntersection(target, source); |
| 63 | + |
| 64 | + const sourcePos = getEdgePosition(source, sourceIntersectionPoint); |
| 65 | + const targetPos = getEdgePosition(target, targetIntersectionPoint); |
| 66 | + |
| 67 | + return { |
| 68 | + sx: sourceIntersectionPoint.x, |
| 69 | + sy: sourceIntersectionPoint.y, |
| 70 | + tx: targetIntersectionPoint.x, |
| 71 | + ty: targetIntersectionPoint.y, |
| 72 | + sourcePos, |
| 73 | + targetPos, |
| 74 | + }; |
| 75 | +} |
0 commit comments