-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
90 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,82 +1,100 @@ | ||
import { Position } from '@xyflow/react'; | ||
import { Position, MarkerType } from '@xyflow/react'; | ||
|
||
// returns the position (top,right,bottom or right) passed node compared to | ||
function getParams(nodeA: any, nodeB: any) { | ||
const centerA = getNodeCenter(nodeA); | ||
const centerB = getNodeCenter(nodeB); | ||
// this helper function returns the intersection point | ||
// of the line between the center of the intersectionNode and the target node | ||
function getNodeIntersection(intersectionNode: any, targetNode: any) { | ||
// https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a | ||
const { width: intersectionNodeWidth, height: intersectionNodeHeight } = | ||
intersectionNode.measured; | ||
const intersectionNodePosition = intersectionNode.internals.positionAbsolute; | ||
const targetPosition = targetNode.internals.positionAbsolute; | ||
|
||
const horizontalDiff = Math.abs(centerA.x - centerB.x); | ||
const verticalDiff = Math.abs(centerA.y - centerB.y); | ||
const w = intersectionNodeWidth / 2; | ||
const h = intersectionNodeHeight / 2; | ||
|
||
let position; | ||
const x2 = intersectionNodePosition.x + w; | ||
const y2 = intersectionNodePosition.y + h; | ||
const x1 = targetPosition.x + targetNode.measured.width / 2; | ||
const y1 = targetPosition.y + targetNode.measured.height / 2; | ||
|
||
// when the horizontal difference between the nodes is bigger, we use Position.Left or Position.Right for the handle | ||
if (horizontalDiff > verticalDiff) { | ||
position = centerA.x > centerB.x ? Position.Left : Position.Right; | ||
} else { | ||
// here the vertical difference between the nodes is bigger, so we use Position.Top or Position.Bottom for the handle | ||
position = centerA.y > centerB.y ? Position.Top : Position.Bottom; | ||
} | ||
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; | ||
|
||
const [x, y] = getHandleCoordsByPosition(nodeA, position); | ||
return [x, y, position]; | ||
return { x, y }; | ||
} | ||
|
||
function getHandleCoordsByPosition(node: any, handlePosition: any) { | ||
// all handles are from type source, that's why we use handleBounds.source here | ||
const handle = node.internals.handleBounds.source.find( | ||
(h: any) => h.position === handlePosition, | ||
); | ||
console.log('nodegetHandleCoordsByPosition', node); | ||
let offsetX = handle.width / 2; | ||
let offsetY = handle.height / 2; | ||
// returns the position (top,right,bottom or right) passed node compared to the intersection point | ||
function getEdgePosition(node: any, intersectionPoint: any) { | ||
const n = { ...node.internals.positionAbsolute, ...node }; | ||
const nx = Math.round(n.x); | ||
const ny = Math.round(n.y); | ||
const px = Math.round(intersectionPoint.x); | ||
const py = Math.round(intersectionPoint.y); | ||
|
||
// this is a tiny detail to make the markerEnd of an edge visible. | ||
// The handle position that gets calculated has the origin top-left, so depending which side we are using, we add a little offset | ||
// when the handlePosition is Position.Right for example, we need to add an offset as big as the handle itself in order to get the correct position | ||
switch (handlePosition) { | ||
case Position.Left: | ||
offsetX = 0; | ||
console.log('Position Left'); | ||
break; | ||
case Position.Right: | ||
offsetX = handle.width; | ||
console.log('Position Right'); | ||
break; | ||
case Position.Top: | ||
offsetY = 0; | ||
console.log('Position Top'); | ||
break; | ||
case Position.Bottom: | ||
console.log('Position Bottom'); | ||
offsetY = handle.height; | ||
break; | ||
if (px <= nx + 1) { | ||
return Position.Left; | ||
} | ||
if (px >= nx + n.measured.width - 1) { | ||
return Position.Right; | ||
} | ||
if (py <= ny + 1) { | ||
return Position.Top; | ||
} | ||
if (py >= n.y + n.measured.height - 1) { | ||
return Position.Bottom; | ||
} | ||
|
||
const x = node.internals.positionAbsolute.x + handle.x + offsetX; | ||
const y = node.internals.positionAbsolute.y + handle.y + offsetY; | ||
|
||
return [x, y]; | ||
} | ||
|
||
function getNodeCenter(node: any) { | ||
return { | ||
x: node.internals.positionAbsolute.x + node.measured.width / 2, | ||
y: node.internals.positionAbsolute.y + node.measured.height / 2, | ||
}; | ||
return Position.Top; | ||
} | ||
|
||
// returns the parameters (sx, sy, tx, ty, sourcePos, targetPos) you need to create an edge | ||
export function getEdgeParams(source: any, target: any) { | ||
const [sx, sy, sourcePos] = getParams(source, target); | ||
const [tx, ty, targetPos] = getParams(target, source); | ||
const sourceIntersectionPoint = getNodeIntersection(source, target); | ||
const targetIntersectionPoint = getNodeIntersection(target, source); | ||
|
||
const sourcePos = getEdgePosition(source, sourceIntersectionPoint); | ||
const targetPos = getEdgePosition(target, targetIntersectionPoint); | ||
|
||
return { | ||
sx, | ||
sy, | ||
tx, | ||
ty, | ||
sx: sourceIntersectionPoint.x, | ||
sy: sourceIntersectionPoint.y, | ||
tx: targetIntersectionPoint.x, | ||
ty: targetIntersectionPoint.y, | ||
sourcePos, | ||
targetPos, | ||
}; | ||
} | ||
|
||
// export function createNodesAndEdges() { | ||
// const nodes = []; | ||
// const edges = []; | ||
// const center = { x: window.innerWidth / 2, y: window.innerHeight / 2 }; | ||
// | ||
// nodes.push({ id: 'target', data: { label: 'Target' }, position: center }); | ||
// | ||
// for (let i = 0; i < 8; i++) { | ||
// const degrees = i * (360 / 8); | ||
// const radians = degrees * (Math.PI / 180); | ||
// const x = 250 * Math.cos(radians) + center.x; | ||
// const y = 250 * Math.sin(radians) + center.y; | ||
// | ||
// nodes.push({ id: `${i}`, data: { label: 'Source' }, position: { x, y } }); | ||
// | ||
// edges.push({ | ||
// id: `edge-${i}`, | ||
// target: 'target', | ||
// source: `${i}`, | ||
// type: 'floating', | ||
// markerEnd: { | ||
// type: MarkerType.Arrow, | ||
// }, | ||
// }); | ||
// } | ||
// | ||
// return { nodes, edges }; | ||
// } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters