refactor Bezier function
This commit is contained in:
@@ -82,6 +82,8 @@ export default ({
|
|||||||
targetY: number,
|
targetY: number,
|
||||||
targetPosition: Position | undefined;
|
targetPosition: Position | undefined;
|
||||||
|
|
||||||
|
let toCurvature: 'sourceCurvature' | 'targetCurvature';
|
||||||
|
|
||||||
switch (connectionHandleType) {
|
switch (connectionHandleType) {
|
||||||
case 'source':
|
case 'source':
|
||||||
{
|
{
|
||||||
@@ -91,6 +93,7 @@ export default ({
|
|||||||
targetX = toX;
|
targetX = toX;
|
||||||
targetY = toY;
|
targetY = toY;
|
||||||
targetPosition = toPosition;
|
targetPosition = toPosition;
|
||||||
|
toCurvature = 'targetCurvature';
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'target':
|
case 'target':
|
||||||
@@ -101,6 +104,7 @@ export default ({
|
|||||||
targetX = fromX;
|
targetX = fromX;
|
||||||
targetY = fromY;
|
targetY = fromY;
|
||||||
targetPosition = fromPosition;
|
targetPosition = fromPosition;
|
||||||
|
toCurvature = 'sourceCurvature';
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -139,9 +143,10 @@ export default ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (connectionLineType === ConnectionLineType.Bezier) {
|
if (connectionLineType === ConnectionLineType.Bezier) {
|
||||||
// @TODO: we need another getBezier function, that handles a connection line.
|
// we don't know the destination position, so we can zero the to curvature
|
||||||
// Since we don't know the target position, we can't use the default bezier function here.
|
dAttr = getBezierPath({ ...pathParams, [toCurvature]: 0 });
|
||||||
dAttr = getBezierPath({ ...pathParams, curvature: 0 });
|
// or we assume the destination position is opposite to the source position
|
||||||
|
// dAttr = getBezierPath(pathParams);
|
||||||
} else if (connectionLineType === ConnectionLineType.Step) {
|
} else if (connectionLineType === ConnectionLineType.Step) {
|
||||||
dAttr = getSmoothStepPath({
|
dAttr = getSmoothStepPath({
|
||||||
...pathParams,
|
...pathParams,
|
||||||
|
|||||||
@@ -11,12 +11,78 @@ export interface GetBezierPathParams {
|
|||||||
targetY: number;
|
targetY: number;
|
||||||
targetPosition?: Position;
|
targetPosition?: Position;
|
||||||
curvature?: number;
|
curvature?: number;
|
||||||
|
sourceCurvature?: number;
|
||||||
|
targetCurvature?: number;
|
||||||
centerX?: number;
|
centerX?: number;
|
||||||
centerY?: number;
|
centerY?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @TODO: refactor getBezierPath function. It's too long and hard to understand.
|
interface GetControlWithCurvatureParams {
|
||||||
// We should reuse the curvature handling for top/bottom and left/right.
|
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({
|
export function getBezierPath({
|
||||||
sourceX,
|
sourceX,
|
||||||
sourceY,
|
sourceY,
|
||||||
@@ -25,78 +91,35 @@ export function getBezierPath({
|
|||||||
targetY,
|
targetY,
|
||||||
targetPosition = Position.Top,
|
targetPosition = Position.Top,
|
||||||
curvature = 0.25,
|
curvature = 0.25,
|
||||||
|
sourceCurvature = curvature,
|
||||||
|
targetCurvature = curvature,
|
||||||
centerX,
|
centerX,
|
||||||
centerY,
|
centerY,
|
||||||
}: GetBezierPathParams): string {
|
}: GetBezierPathParams): string {
|
||||||
const leftAndRight = [Position.Left, Position.Right];
|
|
||||||
const hasCurvature = curvature > 0;
|
|
||||||
const [_centerX, _centerY] = getCenter({ sourceX, sourceY, targetX, targetY });
|
const [_centerX, _centerY] = getCenter({ sourceX, sourceY, targetX, targetY });
|
||||||
|
centerX = centerX ?? _centerX;
|
||||||
if (leftAndRight.includes(sourcePosition) && leftAndRight.includes(targetPosition)) {
|
centerY = centerY ?? _centerY;
|
||||||
const cX = typeof centerX !== 'undefined' ? centerX : _centerX;
|
const [sourceControlX, sourceControlY] = getControlWithCurvature({
|
||||||
const distanceX = targetX - sourceX;
|
pos: sourcePosition,
|
||||||
const absDistanceX = Math.abs(distanceX);
|
x1: sourceX,
|
||||||
const amtX = (Math.sqrt(absDistanceX) / 2) * (50 * curvature);
|
y1: sourceY,
|
||||||
|
x2: targetX,
|
||||||
let hx1 = cX;
|
y2: targetY,
|
||||||
let hx2 = cX;
|
cX: centerX,
|
||||||
|
cY: centerY,
|
||||||
if (hasCurvature) {
|
c: sourceCurvature,
|
||||||
const sourceAndTargetRight = sourcePosition === Position.Right && targetPosition === Position.Right;
|
});
|
||||||
const sourceAndTargetLeft = sourcePosition === Position.Left && targetPosition === Position.Left;
|
const [targetControlX, targetControlY] = getControlWithCurvature({
|
||||||
|
pos: targetPosition,
|
||||||
hx1 = sourceX + amtX;
|
x1: targetX,
|
||||||
hx2 = targetX - amtX;
|
y1: targetY,
|
||||||
|
x2: sourceX,
|
||||||
if (sourceAndTargetLeft) {
|
y2: sourceY,
|
||||||
hx1 = sourceX - amtX;
|
cX: centerX,
|
||||||
} else if (sourceAndTargetRight) {
|
cY: centerY,
|
||||||
hx2 = targetX + amtX;
|
c: targetCurvature,
|
||||||
} else if (sourcePosition === Position.Left && targetX <= sourceX) {
|
});
|
||||||
hx1 = cX;
|
return `M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`;
|
||||||
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 default memo(
|
export default memo(
|
||||||
|
|||||||
@@ -1,12 +1,121 @@
|
|||||||
import React, { memo } from 'react';
|
import React, { memo } from 'react';
|
||||||
|
import { EdgeProps, Position } from '../../types';
|
||||||
|
import BaseEdge from './BaseEdge';
|
||||||
|
import { getCenter } from './utils';
|
||||||
|
|
||||||
import BezierEdge, { getBezierPath, GetBezierPathParams } from './BezierEdge';
|
export interface GetSimpleBezierPathParams {
|
||||||
import { EdgeProps } from '../../types';
|
sourceX: number;
|
||||||
|
sourceY: number;
|
||||||
export function getSimpleBezierPath(props: GetBezierPathParams): string {
|
sourcePosition?: Position;
|
||||||
return getBezierPath({ ...props, curvature: 0 });
|
targetX: number;
|
||||||
|
targetY: number;
|
||||||
|
targetPosition?: Position;
|
||||||
|
centerX?: number;
|
||||||
|
centerY?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo((props: EdgeProps) => {
|
interface GetControlParams {
|
||||||
return <BezierEdge {...props} curvature={0} />;
|
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 (
|
||||||
|
<BaseEdge
|
||||||
|
path={path}
|
||||||
|
centerX={centerX}
|
||||||
|
centerY={centerY}
|
||||||
|
label={label}
|
||||||
|
labelStyle={labelStyle}
|
||||||
|
labelShowBg={labelShowBg}
|
||||||
|
labelBgStyle={labelBgStyle}
|
||||||
|
labelBgPadding={labelBgPadding}
|
||||||
|
labelBgBorderRadius={labelBgBorderRadius}
|
||||||
|
style={style}
|
||||||
|
markerEnd={markerEnd}
|
||||||
|
markerStart={markerStart}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user