From 18758facbd8272fa25a51005a721a2f7d75c31c5 Mon Sep 17 00:00:00 2001 From: Braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Fri, 25 Mar 2022 14:59:07 +0100 Subject: [PATCH] feat(edges): Add & export bezierCenter function --- src/components/Edges/utils.ts | 153 ++++++++++++++++++---------------- src/index.ts | 2 +- 2 files changed, 83 insertions(+), 72 deletions(-) diff --git a/src/components/Edges/utils.ts b/src/components/Edges/utils.ts index de75a9ee..6cff639a 100644 --- a/src/components/Edges/utils.ts +++ b/src/components/Edges/utils.ts @@ -1,4 +1,4 @@ -import { GetBezierPathParams, GetCenterParams, GetSmoothStepPathParams, Position } from '~/types' +import { GetBezierPathParams, GetCenterParams, GetControlWithCurvatureParams, GetSmoothStepPathParams, Position } from '~/types' const LeftOrRight = [Position.Left, Position.Right] @@ -36,6 +36,37 @@ export const getCenter = ({ return [centerX, centerY, xOffset, yOffset] } +function calculateControlOffset(distance: number, curvature: number): number { + if (distance >= 0) { + return 0.5 * distance + } else { + return curvature * 25 * Math.sqrt(-distance) + } +} + +function getControlWithCurvature({ pos, x1, y1, x2, y2, c }: GetControlWithCurvatureParams): [number, number] { + let ctX: number, ctY: number + switch (pos) { + case Position.Left: + ctX = x1 - calculateControlOffset(x1 - x2, c) + ctY = y1 + break + case Position.Right: + ctX = x1 + calculateControlOffset(x2 - x1, c) + ctY = y1 + break + case Position.Top: + ctX = x1 + ctY = y1 - calculateControlOffset(y1 - y2, c) + break + case Position.Bottom: + ctX = x1 + ctY = y1 + calculateControlOffset(y2 - y1, c) + break + } + return [ctX, ctY] +} + export function getBezierPath({ sourceX, sourceY, @@ -44,78 +75,58 @@ export function getBezierPath({ targetY, targetPosition = Position.Top, curvature = 0.25, - centerX, - centerY, }: GetBezierPathParams): string { - const leftAndRight = [Position.Left, Position.Right] - const hasCurvature = curvature > 0 - const [_centerX, _centerY] = getCenter({ sourceX, sourceY, targetX, targetY }) + const [sourceControlX, sourceControlY] = getControlWithCurvature({ + pos: sourcePosition, + x1: sourceX, + y1: sourceY, + x2: targetX, + y2: targetY, + c: curvature, + }) + const [targetControlX, targetControlY] = getControlWithCurvature({ + pos: targetPosition, + x1: targetX, + y1: targetY, + x2: sourceX, + y2: sourceY, + c: curvature, + }) + return `M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}` +} - if (leftAndRight.includes(sourcePosition) && leftAndRight.includes(targetPosition)) { - const cX = typeof centerX !== 'undefined' ? centerX : _centerX - const distanceX = targetX - sourceX - const absDistanceX = Math.abs(distanceX) - const amtX = (Math.sqrt(absDistanceX) / 2) * (50 * curvature) - - let hx1 = cX - let hx2 = cX - - if (hasCurvature) { - const sourceAndTargetRight = sourcePosition === Position.Right && targetPosition === Position.Right - const sourceAndTargetLeft = sourcePosition === Position.Left && targetPosition === Position.Left - - hx1 = sourceX + amtX - hx2 = targetX - amtX - - if (sourceAndTargetLeft) { - hx1 = sourceX - amtX - } else if (sourceAndTargetRight) { - hx2 = targetX + amtX - } else if (sourcePosition === Position.Left && targetX <= sourceX) { - hx1 = cX - hx2 = cX - } else if (sourcePosition === Position.Left && targetX > sourceX) { - hx1 = sourceX - amtX - hx2 = targetX + amtX - } - } - - return `M${sourceX},${sourceY} C${hx1},${sourceY} ${hx2},${targetY}, ${targetX},${targetY}` - } else if (leftAndRight.includes(targetPosition)) { - return `M${sourceX},${sourceY} Q${sourceX},${targetY} ${targetX},${targetY}` - } else if (leftAndRight.includes(sourcePosition)) { - return `M${sourceX},${sourceY} Q${targetX},${sourceY} ${targetX},${targetY}` - } - - const cY = typeof centerY !== 'undefined' ? centerY : _centerY - const distanceY = targetY - sourceY - const absDistanceY = Math.abs(distanceY) - const amtY = (Math.sqrt(absDistanceY) / 2) * (50 * curvature) - - let hy1 = cY - let hy2 = cY - - if (hasCurvature) { - hy1 = sourceY + amtY - hy2 = targetY - amtY - - const sourceAndTargetTop = sourcePosition === Position.Top && targetPosition === Position.Top - const sourceAndTargetBottom = sourcePosition === Position.Bottom && targetPosition === Position.Bottom - - if (sourceAndTargetTop) { - hy1 = targetY - amtY - } else if (sourceAndTargetBottom) { - hy2 = targetY + amtY - } else if (sourcePosition === Position.Top && targetY <= sourceY) { - hy1 = cY - hy2 = cY - } else if (sourcePosition === Position.Top && targetY > sourceY) { - hy1 = sourceY - amtY - hy2 = targetY + amtY - } - } - - return `M${sourceX},${sourceY} C${sourceX},${hy1} ${targetX},${hy2} ${targetX},${targetY}` +export function getBezierCenter({ + sourceX, + sourceY, + sourcePosition = Position.Bottom, + targetX, + targetY, + targetPosition = Position.Top, + curvature = 0.25, +}: GetBezierPathParams): [number, number, number, number] { + const [sourceControlX, sourceControlY] = getControlWithCurvature({ + pos: sourcePosition, + x1: sourceX, + y1: sourceY, + x2: targetX, + y2: targetY, + c: curvature, + }) + const [targetControlX, targetControlY] = getControlWithCurvature({ + pos: targetPosition, + x1: targetX, + y1: targetY, + x2: sourceX, + y2: sourceY, + c: curvature, + }) + // cubic bezier t=0.5 mid point, not the actual mid point, but easy to calculate + // https://stackoverflow.com/questions/67516101/how-to-find-distance-mid-point-of-bezier-curve + const centerX = sourceX * 0.125 + sourceControlX * 0.375 + targetControlX * 0.375 + targetX * 0.125 + const centerY = sourceY * 0.125 + sourceControlY * 0.375 + targetControlY * 0.375 + targetY * 0.125 + const xOffset = Math.abs(centerX - sourceX) + const yOffset = Math.abs(centerY - sourceY) + return [centerX, centerY, xOffset, yOffset] } // These are some helper methods for drawing the round corners diff --git a/src/index.ts b/src/index.ts index 0ca33440..fd68aed4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ export { default as VueFlow } from './container/VueFlow/VueFlow.vue' export { default as Handle } from './components/Handle/Handle.vue' -export { getBezierPath, getSmoothStepPath, getCenter as getEdgeCenter } from './components/Edges/utils' +export { getBezierPath, getSmoothStepPath, getCenter as getEdgeCenter, getBezierCenter } from './components/Edges/utils' export { isNode,