diff --git a/src/components/ConnectionLine/index.tsx b/src/components/ConnectionLine/index.tsx
index 895df605..be358821 100644
--- a/src/components/ConnectionLine/index.tsx
+++ b/src/components/ConnectionLine/index.tsx
@@ -82,6 +82,8 @@ export default ({
targetY: number,
targetPosition: Position | undefined;
+ let toCurvature: 'sourceCurvature' | 'targetCurvature';
+
switch (connectionHandleType) {
case 'source':
{
@@ -91,6 +93,7 @@ export default ({
targetX = toX;
targetY = toY;
targetPosition = toPosition;
+ toCurvature = 'targetCurvature';
}
break;
case 'target':
@@ -101,6 +104,7 @@ export default ({
targetX = fromX;
targetY = fromY;
targetPosition = fromPosition;
+ toCurvature = 'sourceCurvature';
}
break;
}
@@ -139,9 +143,10 @@ export default ({
};
if (connectionLineType === ConnectionLineType.Bezier) {
- // @TODO: we need another getBezier function, that handles a connection line.
- // Since we don't know the target position, we can't use the default bezier function here.
- dAttr = getBezierPath({ ...pathParams, curvature: 0 });
+ // we don't know the destination position, so we can zero the to curvature
+ dAttr = getBezierPath({ ...pathParams, [toCurvature]: 0 });
+ // or we assume the destination position is opposite to the source position
+ // dAttr = getBezierPath(pathParams);
} else if (connectionLineType === ConnectionLineType.Step) {
dAttr = getSmoothStepPath({
...pathParams,
diff --git a/src/components/Edges/BezierEdge.tsx b/src/components/Edges/BezierEdge.tsx
index 36e31486..df9ab8fd 100644
--- a/src/components/Edges/BezierEdge.tsx
+++ b/src/components/Edges/BezierEdge.tsx
@@ -11,12 +11,78 @@ export interface GetBezierPathParams {
targetY: number;
targetPosition?: Position;
curvature?: number;
+ sourceCurvature?: number;
+ targetCurvature?: number;
centerX?: number;
centerY?: number;
}
-// @TODO: refactor getBezierPath function. It's too long and hard to understand.
-// We should reuse the curvature handling for top/bottom and left/right.
+interface GetControlWithCurvatureParams {
+ pos: Position;
+ x1: number;
+ y1: number;
+ x2: number;
+ y2: number;
+ cX: number;
+ cY: number;
+ c: number;
+}
+
+function calculateControlOffset(distance: number, curvature: number): number {
+ return curvature * 25 * Math.sqrt(distance);
+}
+
+function getControlWithCurvature({ pos, x1, y1, x2, y2, cX, cY, c }: GetControlWithCurvatureParams): [number, number] {
+ let ctX: number, ctY: number;
+ switch (pos) {
+ case Position.Left:
+ {
+ const d = x2 - x1;
+ ctY = y1;
+ if (d <= 0) {
+ ctX = cX;
+ } else {
+ ctX = x1 - calculateControlOffset(d, c);
+ }
+ }
+ break;
+ case Position.Right:
+ {
+ const d = x1 - x2;
+ ctY = y1;
+ if (d <= 0) {
+ ctX = cX;
+ } else {
+ ctX = x1 + calculateControlOffset(d, c);
+ }
+ }
+ break;
+ case Position.Top:
+ {
+ const d = y2 - y1;
+ ctX = x1;
+ if (d <= 0) {
+ ctY = cY;
+ } else {
+ ctY = y1 - calculateControlOffset(d, c);
+ }
+ }
+ break;
+ case Position.Bottom:
+ {
+ const d = y1 - y2;
+ ctX = x1;
+ if (d <= 0) {
+ ctY = cY;
+ } else {
+ ctY = y1 + calculateControlOffset(d, c);
+ }
+ }
+ break;
+ }
+ return [ctX, ctY];
+}
+
export function getBezierPath({
sourceX,
sourceY,
@@ -25,78 +91,35 @@ export function getBezierPath({
targetY,
targetPosition = Position.Top,
curvature = 0.25,
+ sourceCurvature = curvature,
+ targetCurvature = curvature,
centerX,
centerY,
}: GetBezierPathParams): string {
- const leftAndRight = [Position.Left, Position.Right];
- const hasCurvature = curvature > 0;
const [_centerX, _centerY] = getCenter({ sourceX, sourceY, 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}`;
+ centerX = centerX ?? _centerX;
+ centerY = centerY ?? _centerY;
+ const [sourceControlX, sourceControlY] = getControlWithCurvature({
+ pos: sourcePosition,
+ x1: sourceX,
+ y1: sourceY,
+ x2: targetX,
+ y2: targetY,
+ cX: centerX,
+ cY: centerY,
+ c: sourceCurvature,
+ });
+ const [targetControlX, targetControlY] = getControlWithCurvature({
+ pos: targetPosition,
+ x1: targetX,
+ y1: targetY,
+ x2: sourceX,
+ y2: sourceY,
+ cX: centerX,
+ cY: centerY,
+ c: targetCurvature,
+ });
+ return `M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`;
}
export default memo(
diff --git a/src/components/Edges/SimpleBezierEdge.tsx b/src/components/Edges/SimpleBezierEdge.tsx
index 1bc671c9..7338e557 100644
--- a/src/components/Edges/SimpleBezierEdge.tsx
+++ b/src/components/Edges/SimpleBezierEdge.tsx
@@ -1,12 +1,121 @@
import React, { memo } from 'react';
+import { EdgeProps, Position } from '../../types';
+import BaseEdge from './BaseEdge';
+import { getCenter } from './utils';
-import BezierEdge, { getBezierPath, GetBezierPathParams } from './BezierEdge';
-import { EdgeProps } from '../../types';
-
-export function getSimpleBezierPath(props: GetBezierPathParams): string {
- return getBezierPath({ ...props, curvature: 0 });
+export interface GetSimpleBezierPathParams {
+ sourceX: number;
+ sourceY: number;
+ sourcePosition?: Position;
+ targetX: number;
+ targetY: number;
+ targetPosition?: Position;
+ centerX?: number;
+ centerY?: number;
}
-export default memo((props: EdgeProps) => {
- return ;
-});
+interface GetControlParams {
+ pos: Position;
+ x: number;
+ y: number;
+ cX: number;
+ cY: number;
+}
+
+function getControl({ pos, x, y, cX, cY }: GetControlParams): [number, number] {
+ let ctX: number, ctY: number;
+ switch (pos) {
+ case Position.Left:
+ case Position.Right:
+ {
+ ctX = cX;
+ ctY = y;
+ }
+ break;
+ case Position.Top:
+ case Position.Bottom:
+ {
+ ctX = x;
+ ctY = cY;
+ }
+ break;
+ }
+ return [ctX, ctY];
+}
+
+export function getSimpleBezierPath({
+ sourceX,
+ sourceY,
+ sourcePosition = Position.Bottom,
+ targetX,
+ targetY,
+ targetPosition = Position.Top,
+ centerX,
+ centerY,
+}: GetSimpleBezierPathParams): string {
+ const [_centerX, _centerY] = getCenter({ sourceX, sourceY, targetX, targetY });
+ centerX = centerX ?? _centerX;
+ centerY = centerY ?? _centerY;
+ const [sourceControlX, sourceControlY] = getControl({
+ pos: sourcePosition,
+ x: sourceX,
+ y: sourceY,
+ cX: centerX,
+ cY: centerY,
+ });
+ const [targetControlX, targetControlY] = getControl({
+ pos: targetPosition,
+ x: targetX,
+ y: targetY,
+ cX: centerX,
+ cY: centerY,
+ });
+ return `M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`;
+}
+
+export default memo(
+ ({
+ sourceX,
+ sourceY,
+ targetX,
+ targetY,
+ sourcePosition = Position.Bottom,
+ targetPosition = Position.Top,
+ label,
+ labelStyle,
+ labelShowBg,
+ labelBgStyle,
+ labelBgPadding,
+ labelBgBorderRadius,
+ style,
+ markerEnd,
+ markerStart,
+ }: EdgeProps) => {
+ const [centerX, centerY] = getCenter({ sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition });
+ const path = getSimpleBezierPath({
+ sourceX,
+ sourceY,
+ sourcePosition,
+ targetX,
+ targetY,
+ targetPosition,
+ });
+
+ return (
+
+ );
+ }
+);