178 lines
4.5 KiB
TypeScript
178 lines
4.5 KiB
TypeScript
import React, { memo } from 'react';
|
|
import { EdgeProps, Position } from '../../types';
|
|
import BaseEdge from './BaseEdge';
|
|
|
|
export interface GetBezierPathParams {
|
|
sourceX: number;
|
|
sourceY: number;
|
|
sourcePosition?: Position;
|
|
targetX: number;
|
|
targetY: number;
|
|
targetPosition?: Position;
|
|
curvature?: number;
|
|
}
|
|
|
|
interface GetControlWithCurvatureParams {
|
|
pos: Position;
|
|
x1: number;
|
|
y1: number;
|
|
x2: number;
|
|
y2: number;
|
|
c: number;
|
|
}
|
|
|
|
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,
|
|
sourcePosition = Position.Bottom,
|
|
targetX,
|
|
targetY,
|
|
targetPosition = Position.Top,
|
|
curvature = 0.25,
|
|
}: GetBezierPathParams): string {
|
|
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}`;
|
|
}
|
|
|
|
// @TODO: this function will recalculate the control points
|
|
// one option is to let getXXXPath() return center points
|
|
// but will introduce breaking changes
|
|
// the getCenter() of other types of edges might need to change, too
|
|
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];
|
|
}
|
|
|
|
export default memo(
|
|
({
|
|
sourceX,
|
|
sourceY,
|
|
targetX,
|
|
targetY,
|
|
sourcePosition = Position.Bottom,
|
|
targetPosition = Position.Top,
|
|
label,
|
|
labelStyle,
|
|
labelShowBg,
|
|
labelBgStyle,
|
|
labelBgPadding,
|
|
labelBgBorderRadius,
|
|
style,
|
|
markerEnd,
|
|
markerStart,
|
|
curvature,
|
|
}: EdgeProps) => {
|
|
const params = {
|
|
sourceX,
|
|
sourceY,
|
|
sourcePosition,
|
|
targetX,
|
|
targetY,
|
|
targetPosition,
|
|
curvature,
|
|
};
|
|
const path = getBezierPath(params);
|
|
const [centerX, centerY] = getBezierCenter(params);
|
|
|
|
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}
|
|
/>
|
|
);
|
|
}
|
|
);
|