import { FC, useMemo } from 'react'; import cc from 'classcat'; import { EdgeProps, getMarkerEnd, useStoreState, Position, getBezierPath, ConnectionLineComponentProps, } from 'react-flow-renderer'; function getNodeIntersection(source: any, target: any) { // https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a const w = source.__rf.width / 2; const h = source.__rf.height / 2; const x2 = source.__rf.position.x + w; const y2 = source.__rf.position.y + h; const x1 = target.__rf.position.x; const y1 = target.__rf.position.y; const xx1 = (x1 - x2) / (2 * w) - (y1 - y2) / (2 * h); const yy1 = (x1 - x2) / (2 * w) + (y1 - y2) / (2 * h); const a = 1 / (Math.abs(xx1) + Math.abs(yy1)); const xx3 = a * xx1; const yy3 = a * yy1; const x = w * (xx3 + yy3) + x2; const y = h * (-xx3 + yy3) + y2; return [x, y]; } function getEdgePosition(node: any, x: number, y: number) { const n = { ...node.__rf.position, ...node.__rf }; const nx = Math.round(n.x); const ny = Math.round(n.y); const px = Math.round(x); const py = Math.round(y); if (px <= nx + 1) { return Position.Left; } if (px >= nx + n.width - 1) { return Position.Right; } if (py <= ny + 1) { return Position.Top; } if (py >= n.y + n.height - 1) { return Position.Bottom; } return Position.Top; } function getArrow(source: any, target: any) { const [sx, sy] = getNodeIntersection(source, target); const [tx, ty] = getNodeIntersection(target, source); const sourcePos = getEdgePosition(source, sx, sy); const targetPos = getEdgePosition(target, tx, ty); return { sx, sy, tx, ty, label: null, sourcePos, targetPos, }; } const CustomEdge: FC = ({ id, source, target, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, data, arrowHeadType, markerEndId, style, }) => { const nodes = useStoreState((state) => state.nodes); const markerEnd = getMarkerEnd(arrowHeadType, markerEndId); const sourceNode = useMemo(() => nodes.find((n) => n.id === source), [source, nodes]); const targetNode = useMemo(() => nodes.find((n) => n.id === target), [target, nodes]); if (!sourceNode || !targetNode) { return null; } const { sx, sy, tx, ty, label, sourcePos, targetPos } = getArrow(sourceNode, targetNode); const d = getBezierPath({ sourceX: sx, sourceY: sy, sourcePosition: sourcePos, targetPosition: targetPos, targetX: tx, targetY: ty, }); return ( <> {label && ( {label} )} ); }; export const ConnectionLine: FC = ({ targetX, targetY, sourcePosition, targetPosition, sourceNode, }) => { if (!sourceNode) { return null; } const targetNode = { __rf: { width: 1, height: 1, position: { x: targetX, y: targetY } } }; const { sx, sy } = getArrow(sourceNode, targetNode); const d = getBezierPath({ sourceX: sx, sourceY: sy, sourcePosition, targetPosition, targetX, targetY, }); return ( ); }; export default CustomEdge;