You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
521 lines
24 KiB
521 lines
24 KiB
import { isEqual, isNumber } from '@antv/util';
|
|
import { getBBoxHeight, getBBoxSize, getBBoxWidth, getNearestBoundarySide, getNodeBBox } from './bbox';
|
|
import { isCollapsed } from './collapsibility';
|
|
import { getAllPorts, getNodeConnectionPoint, getPortConnectionPoint, getPortPosition } from './element';
|
|
import { idOf } from './id';
|
|
import { isCollinear, isHorizontal, moveTo, parsePoint } from './point';
|
|
import { freeJoin } from './router/orth';
|
|
import { add, distance, manhattanDistance, multiply, normalize, perpendicular, subtract } from './vector';
|
|
/**
|
|
* <zh/> 获取标签的位置样式
|
|
*
|
|
* <en/> Get the style of the label's position
|
|
* @param key - <zh/> 边对象 | <en/> The edge object
|
|
* @param placement - <zh/> 标签位置 | <en/> Position of the label
|
|
* @param autoRotate - <zh/> 是否自动旋转 | <en/> Whether to auto-rotate
|
|
* @param offsetX - <zh/> 标签相对于边的水平偏移量 | <en/> Horizontal offset of the label relative to the edge
|
|
* @param offsetY - <zh/> 标签相对于边的垂直偏移量 | <en/> Vertical offset of the label relative to the edge
|
|
* @returns <zh/> 标签的位置样式 | <en/> Returns the style of the label's position
|
|
*/
|
|
export function getLabelPositionStyle(key, placement, autoRotate, offsetX, offsetY) {
|
|
const START_RATIO = 0;
|
|
const MIDDLE_RATIO = 0.5;
|
|
const END_RATIO = 0.99;
|
|
let ratio = typeof placement === 'number' ? placement : MIDDLE_RATIO;
|
|
if (placement === 'start')
|
|
ratio = START_RATIO;
|
|
if (placement === 'end')
|
|
ratio = END_RATIO;
|
|
const point = parsePoint(key.getPoint(ratio));
|
|
const pointOffset = parsePoint(key.getPoint(ratio + 0.01));
|
|
let textAlign = placement === 'start' ? 'left' : placement === 'end' ? 'right' : 'center';
|
|
if (isHorizontal(point, pointOffset) || !autoRotate) {
|
|
const [x, y] = getXYByPlacement(key, ratio, offsetX, offsetY);
|
|
return { transform: [['translate', x, y]], textAlign };
|
|
}
|
|
let angle = Math.atan2(pointOffset[1] - point[1], pointOffset[0] - point[0]);
|
|
const isRevert = pointOffset[0] < point[0];
|
|
if (isRevert) {
|
|
textAlign = textAlign === 'center' ? textAlign : textAlign === 'left' ? 'right' : 'left';
|
|
offsetX *= -1;
|
|
angle += Math.PI;
|
|
}
|
|
const [x, y] = getXYByPlacement(key, ratio, offsetX, offsetY, angle);
|
|
const transform = [
|
|
['translate', x, y],
|
|
['rotate', (angle / Math.PI) * 180],
|
|
];
|
|
return {
|
|
textAlign,
|
|
transform,
|
|
};
|
|
}
|
|
/**
|
|
* <zh/> 获取边上徽标的位置样式
|
|
*
|
|
* <en/> Get the position style of the badge on the edge
|
|
* @param shapeMap - <zh/> 边上的图形映射 | <en/> Shape map on the edge
|
|
* @param placement - <zh/> 徽标位置 | <en/> Badge position
|
|
* @param labelPlacement - <zh/> 标签位置 | <en/> Label position
|
|
* @param offsetX - <zh/> 水平偏移量 | <en/> Horizontal offset
|
|
* @param offsetY - <zh/> 垂直偏移量 | <en/> Vertical offset
|
|
* @returns <zh/> 徽标的位置样式 | <en/> Position style of the badge
|
|
*/
|
|
export function getBadgePositionStyle(shapeMap, placement, labelPlacement, offsetX, offsetY) {
|
|
var _a, _b;
|
|
const badgeWidth = ((_a = shapeMap.badge) === null || _a === void 0 ? void 0 : _a.getGeometryBounds().halfExtents[0]) * 2 || 0;
|
|
const labelWidth = ((_b = shapeMap.label) === null || _b === void 0 ? void 0 : _b.getGeometryBounds().halfExtents[0]) * 2 || 0;
|
|
return getLabelPositionStyle(shapeMap.key, labelPlacement, true, (labelWidth ? (labelWidth / 2 + badgeWidth / 2) * (placement === 'suffix' ? 1 : -1) : 0) + offsetX, offsetY);
|
|
}
|
|
/**
|
|
* <zh/> 获取给定边上的指定位置的坐标
|
|
*
|
|
* <en/> Get the coordinates at the specified position on the given edge
|
|
* @param key - <zh/> 边实例 | <en/> Edge instance
|
|
* @param ratio - <zh/> 位置比率 | <en/> Position ratio
|
|
* @param offsetX - <zh/> 水平偏移量 | <en/> Horizontal offset
|
|
* @param offsetY - <zh/> 垂直偏移量 | <en/> Vertical offset
|
|
* @param angle - <zh/> 旋转角度 | <en/> Rotation angle
|
|
* @returns <zh/> 坐标 | <en/> Coordinates
|
|
*/
|
|
function getXYByPlacement(key, ratio, offsetX, offsetY, angle) {
|
|
const [pointX, pointY] = parsePoint(key.getPoint(ratio));
|
|
let actualOffsetX = offsetX;
|
|
let actualOffsetY = offsetY;
|
|
if (angle) {
|
|
actualOffsetX = offsetX * Math.cos(angle) - offsetY * Math.sin(angle);
|
|
actualOffsetY = offsetX * Math.sin(angle) + offsetY * Math.cos(angle);
|
|
}
|
|
return [pointX + actualOffsetX, pointY + actualOffsetY];
|
|
}
|
|
/** ==================== Curve Edge =========================== */
|
|
/**
|
|
* <zh/> 计算曲线的控制点
|
|
*
|
|
* <en/> Calculate the control point of the curve
|
|
* @param sourcePoint - <zh/> 起点 | <en/> Source point
|
|
* @param targetPoint - <zh/> 终点 | <en/> Target point
|
|
* @param curvePosition - <zh/> 控制点在连线上的相对位置(取值范围为 0-1) | <en/> The relative position of the control point on the line (value range from 0 to 1)
|
|
* @param curveOffset - <zh/> 控制点距离两端点连线的距离 | <en/> The distance between the control point and the line
|
|
* @returns <zh/> 控制点 | <en/> Control points
|
|
*/
|
|
export function getCurveControlPoint(sourcePoint, targetPoint, curvePosition, curveOffset) {
|
|
if (isEqual(sourcePoint, targetPoint))
|
|
return sourcePoint;
|
|
const lineVector = subtract(targetPoint, sourcePoint);
|
|
const controlPoint = [
|
|
sourcePoint[0] + curvePosition * lineVector[0],
|
|
sourcePoint[1] + curvePosition * lineVector[1],
|
|
];
|
|
const perpVector = normalize(perpendicular(lineVector, false));
|
|
controlPoint[0] += curveOffset * perpVector[0];
|
|
controlPoint[1] += curveOffset * perpVector[1];
|
|
return controlPoint;
|
|
}
|
|
/**
|
|
* <zh/> 解析控制点距离两端点连线的距离 `curveOffset`
|
|
*
|
|
* <en/> parse the distance of the control point from the line `curveOffset`
|
|
* @param curveOffset - <zh/> curveOffset | <en/> curveOffset
|
|
* @returns <zh/> 标准 curveOffset | <en/> standard curveOffset
|
|
*/
|
|
export function parseCurveOffset(curveOffset) {
|
|
if (isNumber(curveOffset))
|
|
return [curveOffset, -curveOffset];
|
|
return curveOffset;
|
|
}
|
|
/**
|
|
* <zh/> 解析控制点在两端点连线上的相对位置 `curvePosition`,范围为`0-1`
|
|
*
|
|
* <en/> parse the relative position of the control point on the line `curvePosition`
|
|
* @param curvePosition - <zh/> curvePosition | <en/> curvePosition
|
|
* @returns <zh/> 标准 curvePosition | <en/> standard curvePosition
|
|
*/
|
|
export function parseCurvePosition(curvePosition) {
|
|
if (isNumber(curvePosition))
|
|
return [curvePosition, 1 - curvePosition];
|
|
return curvePosition;
|
|
}
|
|
/**
|
|
* <zh/> 获取二次贝塞尔曲线绘制路径
|
|
*
|
|
* <en/> Calculate the path for drawing a quadratic Bessel curve
|
|
* @param sourcePoint - <zh/> 边的起点 | <en/> Source point
|
|
* @param targetPoint - <zh/> 边的终点 | <en/> Target point
|
|
* @param controlPoint - <zh/> 控制点 | <en/> Control point
|
|
* @returns <zh/> 返回绘制曲线的路径 | <en/> Returns curve path
|
|
*/
|
|
export function getQuadraticPath(sourcePoint, targetPoint, controlPoint) {
|
|
return [
|
|
['M', sourcePoint[0], sourcePoint[1]],
|
|
['Q', controlPoint[0], controlPoint[1], targetPoint[0], targetPoint[1]],
|
|
];
|
|
}
|
|
/**
|
|
* <zh/> 获取三次贝塞尔曲线绘制路径
|
|
*
|
|
* <en/> Calculate the path for drawing a cubic Bessel curve
|
|
* @param sourcePoint - <zh/> 边的起点 | <en/> Source point
|
|
* @param targetPoint - <zh/> 边的终点 | <en/> Target point
|
|
* @param controlPoints - <zh/> 控制点 | <en/> Control point
|
|
* @returns <zh/> 返回绘制曲线的路径 | <en/> Returns curve path
|
|
*/
|
|
export function getCubicPath(sourcePoint, targetPoint, controlPoints) {
|
|
return [
|
|
['M', sourcePoint[0], sourcePoint[1]],
|
|
[
|
|
'C',
|
|
controlPoints[0][0],
|
|
controlPoints[0][1],
|
|
controlPoints[1][0],
|
|
controlPoints[1][1],
|
|
targetPoint[0],
|
|
targetPoint[1],
|
|
],
|
|
];
|
|
}
|
|
/** ==================== Polyline Edge =========================== */
|
|
/**
|
|
* <zh/> 获取折线的绘制路径
|
|
*
|
|
* <en/> Calculates the path for drawing a polyline
|
|
* @param points - <zh/> 折线的顶点 | <en/> The vertices of the polyline
|
|
* @param radius - <zh/> 圆角半径 | <en/> Radius of the rounded corner
|
|
* @param z - <zh/> 路径是否闭合 | <en/> Whether the path is closed
|
|
* @returns <zh/> 返回绘制折线的路径 | <en/> Returns the path for drawing a polyline
|
|
*/
|
|
export function getPolylinePath(points, radius = 0, z = false) {
|
|
const targetIndex = points.length - 1;
|
|
const sourcePoint = points[0];
|
|
const targetPoint = points[targetIndex];
|
|
const controlPoints = points.slice(1, targetIndex);
|
|
const pathArray = [['M', sourcePoint[0], sourcePoint[1]]];
|
|
controlPoints.forEach((midPoint, i) => {
|
|
const prevPoint = controlPoints[i - 1] || sourcePoint;
|
|
const nextPoint = controlPoints[i + 1] || targetPoint;
|
|
if (!isCollinear(prevPoint, midPoint, nextPoint) && radius) {
|
|
const [ps, pt] = getBorderRadiusPoints(prevPoint, midPoint, nextPoint, radius);
|
|
pathArray.push(['L', ps[0], ps[1]], ['Q', midPoint[0], midPoint[1], pt[0], pt[1]], ['L', pt[0], pt[1]]);
|
|
}
|
|
else {
|
|
pathArray.push(['L', midPoint[0], midPoint[1]]);
|
|
}
|
|
});
|
|
pathArray.push(['L', targetPoint[0], targetPoint[1]]);
|
|
if (z)
|
|
pathArray.push(['Z']);
|
|
return pathArray;
|
|
}
|
|
/**
|
|
* <zh/> 根据给定的半径计算出不共线的三点生成贝塞尔曲线的控制点,以模拟接近圆弧
|
|
*
|
|
* <en/> Calculates the control points of the Bezier curve generated by three non-collinear points according to the given radius to simulate an arc
|
|
* @param prevPoint - <zh/> 前一个点 | <en/> Previous point
|
|
* @param midPoint - <zh/> 中间点 | <en/> Middle point
|
|
* @param nextPoint - <zh/> 后一个点 | <en/> Next point
|
|
* @param radius - <zh/> 圆角半径 | <en/> Radius of the rounded corner
|
|
* @returns <zh/> 返回控制点 | <en/> Returns control points
|
|
*/
|
|
export function getBorderRadiusPoints(prevPoint, midPoint, nextPoint, radius) {
|
|
const d0 = manhattanDistance(prevPoint, midPoint);
|
|
const d1 = manhattanDistance(nextPoint, midPoint);
|
|
// 取给定的半径和最小半径之间的较小值 | use the smaller value between the given radius and the minimum radius
|
|
const r = Math.min(radius, Math.min(d0, d1) / 2);
|
|
const ps = [
|
|
midPoint[0] - (r / d0) * (midPoint[0] - prevPoint[0]),
|
|
midPoint[1] - (r / d0) * (midPoint[1] - prevPoint[1]),
|
|
];
|
|
const pt = [
|
|
midPoint[0] - (r / d1) * (midPoint[0] - nextPoint[0]),
|
|
midPoint[1] - (r / d1) * (midPoint[1] - nextPoint[1]),
|
|
];
|
|
return [ps, pt];
|
|
}
|
|
/** ==================== Loop Edge =========================== */
|
|
export const getRadians = (bbox) => {
|
|
const halfPI = Math.PI / 2;
|
|
const halfHeight = getBBoxHeight(bbox) / 2;
|
|
const halfWidth = getBBoxWidth(bbox) / 2;
|
|
const angleWithX = Math.atan2(halfHeight, halfWidth) / 2;
|
|
const angleWithY = Math.atan2(halfWidth, halfHeight) / 2;
|
|
return {
|
|
top: [-halfPI - angleWithY, -halfPI + angleWithY],
|
|
'top-right': [-halfPI + angleWithY, -angleWithX],
|
|
'right-top': [-halfPI + angleWithY, -angleWithX],
|
|
right: [-angleWithX, angleWithX],
|
|
'bottom-right': [angleWithX, halfPI - angleWithY],
|
|
'right-bottom': [angleWithX, halfPI - angleWithY],
|
|
bottom: [halfPI - angleWithY, halfPI + angleWithY],
|
|
'bottom-left': [halfPI + angleWithY, Math.PI - angleWithX],
|
|
'left-bottom': [halfPI + angleWithY, Math.PI - angleWithX],
|
|
left: [Math.PI - angleWithX, Math.PI + angleWithX],
|
|
'top-left': [Math.PI + angleWithX, -halfPI - angleWithY],
|
|
'left-top': [Math.PI + angleWithX, -halfPI - angleWithY],
|
|
};
|
|
};
|
|
/**
|
|
* <zh/> 获取环形边的起点和终点
|
|
*
|
|
* <en/> Get the start and end points of the loop edge
|
|
* @param node - <zh/> 节点实例 | <en/> Node instance
|
|
* @param placement - <zh/> 环形边相对于节点位置 | <en/> Loop position relative to the node
|
|
* @param clockwise - <zh/> 是否顺时针 | <en/> Whether to draw the loop clockwise
|
|
* @param sourcePort - <zh/> 起点连接桩 | <en/> Source port
|
|
* @param targetPort - <zh/> 终点连接桩 | <en/> Target port
|
|
* @returns <zh/> 起点和终点 | <en/> Start and end points
|
|
*/
|
|
export function getLoopEndpoints(node, placement, clockwise, sourcePort, targetPort) {
|
|
const bbox = getNodeBBox(node);
|
|
const center = node.getCenter();
|
|
let sourcePoint = sourcePort && getPortPosition(sourcePort);
|
|
let targetPoint = targetPort && getPortPosition(targetPort);
|
|
if (!sourcePoint || !targetPoint) {
|
|
const radians = getRadians(bbox);
|
|
const angle1 = radians[placement][0];
|
|
const angle2 = radians[placement][1];
|
|
const [width, height] = getBBoxSize(bbox);
|
|
const r = Math.max(width, height);
|
|
const point1 = add(center, [r * Math.cos(angle1), r * Math.sin(angle1), 0]);
|
|
const point2 = add(center, [r * Math.cos(angle2), r * Math.sin(angle2), 0]);
|
|
sourcePoint = getNodeConnectionPoint(node, point1);
|
|
targetPoint = getNodeConnectionPoint(node, point2);
|
|
if (!clockwise) {
|
|
[sourcePoint, targetPoint] = [targetPoint, sourcePoint];
|
|
}
|
|
}
|
|
return [sourcePoint, targetPoint];
|
|
}
|
|
/**
|
|
* <zh/> 获取环形边的绘制路径
|
|
*
|
|
* <en/> Get the path of the loop edge
|
|
* @param node - <zh/> 节点实例 | <en/> Node instance
|
|
* @param placement - <zh/> 环形边相对于节点位置 | <en/> Loop position relative to the node
|
|
* @param clockwise - <zh/> 是否顺时针 | <en/> Whether to draw the loop clockwise
|
|
* @param dist - <zh/> 从节点 keyShape 边缘到自环顶部的距离 | <en/> The distance from the edge of the node keyShape to the top of the self-loop
|
|
* @param sourcePortKey - <zh/> 起点连接桩 key | <en/> Source port key
|
|
* @param targetPortKey - <zh/> 终点连接桩 key | <en/> Target port key
|
|
* @returns <zh/> 返回绘制环形边的路径 | <en/> Returns the path of the loop edge
|
|
*/
|
|
export function getCubicLoopPath(node, placement, clockwise, dist, sourcePortKey, targetPortKey) {
|
|
const sourcePort = node.getPorts()[(sourcePortKey || targetPortKey)];
|
|
const targetPort = node.getPorts()[(targetPortKey || sourcePortKey)];
|
|
// 1. 获取起点和终点 | Get the start and end points
|
|
let [sourcePoint, targetPoint] = getLoopEndpoints(node, placement, clockwise, sourcePort, targetPort);
|
|
// 2. 获取控制点 | Get the control points
|
|
const controlPoints = getCubicLoopControlPoints(node, sourcePoint, targetPoint, dist);
|
|
// 3. 如果定义了连接桩,调整端点以与连接桩边界相交 | If the port is defined, adjust the endpoint to intersect with the port boundary
|
|
if (sourcePort)
|
|
sourcePoint = getPortConnectionPoint(sourcePort, controlPoints[0]);
|
|
if (targetPort)
|
|
targetPoint = getPortConnectionPoint(targetPort, controlPoints.at(-1));
|
|
return getCubicPath(sourcePoint, targetPoint, controlPoints);
|
|
}
|
|
/**
|
|
* <zh/> 获取环形边的控制点
|
|
*
|
|
* <en/> Get the control points of the loop edge
|
|
* @param node - <zh/> 节点实例 | <en/> Node instance
|
|
* @param sourcePoint - <zh/> 起点 | <en/> Source point
|
|
* @param targetPoint - <zh/> 终点 | <en/> Target point
|
|
* @param dist - <zh/> 从节点 keyShape 边缘到自环顶部的距离 | <en/> The distance from the edge of the node keyShape to the top of the self-loop
|
|
* @returns <zh/> 控制点 | <en/> Control points
|
|
*/
|
|
export function getCubicLoopControlPoints(node, sourcePoint, targetPoint, dist) {
|
|
const center = node.getCenter();
|
|
if (isEqual(sourcePoint, targetPoint)) {
|
|
const direction = subtract(sourcePoint, center);
|
|
const adjustment = [
|
|
dist * Math.sign(direction[0]) || dist / 2,
|
|
dist * Math.sign(direction[1]) || -dist / 2,
|
|
0,
|
|
];
|
|
return [add(sourcePoint, adjustment), add(targetPoint, multiply(adjustment, [1, -1, 1]))];
|
|
}
|
|
return [
|
|
moveTo(center, sourcePoint, distance(center, sourcePoint) + dist),
|
|
moveTo(center, targetPoint, distance(center, targetPoint) + dist),
|
|
];
|
|
}
|
|
/**
|
|
* <zh/> 获取环形折线边的绘制路径
|
|
*
|
|
* <en/> Get the path of the loop polyline edge
|
|
* @param node - <zh/> 节点实例 | <en/> Node instance
|
|
* @param radius - <zh/> 圆角半径 | <en/> Radius of the rounded corner
|
|
* @param placement - <zh/> 环形边相对于节点位置 | <en/> Loop position relative to the node
|
|
* @param clockwise - <zh/> 是否顺时针 | <en/> Whether to draw the loop clockwise
|
|
* @param dist - <zh/> 从节点 keyShape 边缘到自环顶部的距离 | <en/> The distance from the edge of the node keyShape to the top of the self-loop
|
|
* @param sourcePortKey - <zh/> 起点连接桩 key | <en/> Source port key
|
|
* @param targetPortKey - <zh/> 终点连接桩 key | <en/> Target port key
|
|
* @returns <zh/> 返回绘制环形折线边的路径 | <en/> Returns the path of the loop polyline edge
|
|
*/
|
|
export function getPolylineLoopPath(node, radius, placement, clockwise, dist, sourcePortKey, targetPortKey) {
|
|
const allPortsMap = getAllPorts(node);
|
|
const sourcePort = allPortsMap[(sourcePortKey || targetPortKey)];
|
|
const targetPort = allPortsMap[(targetPortKey || sourcePortKey)];
|
|
// 1. 获取起点和终点 | Get the start and end points
|
|
let [sourcePoint, targetPoint] = getLoopEndpoints(node, placement, clockwise, sourcePort, targetPort);
|
|
// 2. 获取控制点 | Get the control points
|
|
const controlPoints = getPolylineLoopControlPoints(node, sourcePoint, targetPoint, dist);
|
|
// 3. 如果定义了连接桩,调整端点以与连接桩边界相交 | If the port is defined, adjust the endpoint to intersect with the port boundary
|
|
if (sourcePort)
|
|
sourcePoint = getPortConnectionPoint(sourcePort, controlPoints[0]);
|
|
if (targetPort)
|
|
targetPoint = getPortConnectionPoint(targetPort, controlPoints.at(-1));
|
|
return getPolylinePath([sourcePoint, ...controlPoints, targetPoint], radius);
|
|
}
|
|
/**
|
|
* <zh/> 获取环形折线边的控制点
|
|
*
|
|
* <en/> Get the control points of the loop polyline edge
|
|
* @param node - <zh/> 节点实例 | <en/> Node instance
|
|
* @param sourcePoint - <zh/> 起点 | <en/> Source point
|
|
* @param targetPoint - <zh/> 终点 | <en/> Target point
|
|
* @param dist - <zh/> 从节点 keyShape 边缘到自环顶部的距离 | <en/> The distance from the edge of the node keyShape to the top of the self-loop
|
|
* @returns <zh/> 控制点 | <en/> Control points
|
|
*/
|
|
export function getPolylineLoopControlPoints(node, sourcePoint, targetPoint, dist) {
|
|
const controlPoints = [];
|
|
const bbox = getNodeBBox(node);
|
|
// 1. 起点和终点相同 | The start and end points are the same
|
|
if (isEqual(sourcePoint, targetPoint)) {
|
|
const side = getNearestBoundarySide(sourcePoint, bbox);
|
|
switch (side) {
|
|
case 'left':
|
|
controlPoints.push([sourcePoint[0] - dist, sourcePoint[1]]);
|
|
controlPoints.push([sourcePoint[0] - dist, sourcePoint[1] + dist]);
|
|
controlPoints.push([sourcePoint[0], sourcePoint[1] + dist]);
|
|
break;
|
|
case 'right':
|
|
controlPoints.push([sourcePoint[0] + dist, sourcePoint[1]]);
|
|
controlPoints.push([sourcePoint[0] + dist, sourcePoint[1] + dist]);
|
|
controlPoints.push([sourcePoint[0], sourcePoint[1] + dist]);
|
|
break;
|
|
case 'top':
|
|
controlPoints.push([sourcePoint[0], sourcePoint[1] - dist]);
|
|
controlPoints.push([sourcePoint[0] + dist, sourcePoint[1] - dist]);
|
|
controlPoints.push([sourcePoint[0] + dist, sourcePoint[1]]);
|
|
break;
|
|
case 'bottom':
|
|
controlPoints.push([sourcePoint[0], sourcePoint[1] + dist]);
|
|
controlPoints.push([sourcePoint[0] + dist, sourcePoint[1] + dist]);
|
|
controlPoints.push([sourcePoint[0] + dist, sourcePoint[1]]);
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
const sourceSide = getNearestBoundarySide(sourcePoint, bbox);
|
|
const targetSide = getNearestBoundarySide(targetPoint, bbox);
|
|
// 2. 起点与终点同边 | The start and end points are on the same side
|
|
if (sourceSide === targetSide) {
|
|
const side = sourceSide;
|
|
let x, y;
|
|
switch (side) {
|
|
case 'left':
|
|
x = Math.min(sourcePoint[0], targetPoint[0]) - dist;
|
|
controlPoints.push([x, sourcePoint[1]]);
|
|
controlPoints.push([x, targetPoint[1]]);
|
|
break;
|
|
case 'right':
|
|
x = Math.max(sourcePoint[0], targetPoint[0]) + dist;
|
|
controlPoints.push([x, sourcePoint[1]]);
|
|
controlPoints.push([x, targetPoint[1]]);
|
|
break;
|
|
case 'top':
|
|
y = Math.min(sourcePoint[1], targetPoint[1]) - dist;
|
|
controlPoints.push([sourcePoint[0], y]);
|
|
controlPoints.push([targetPoint[0], y]);
|
|
break;
|
|
case 'bottom':
|
|
y = Math.max(sourcePoint[1], targetPoint[1]) + dist;
|
|
controlPoints.push([sourcePoint[0], y]);
|
|
controlPoints.push([targetPoint[0], y]);
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
// 3. 起点与终点不同边 | The start and end points are on different sides
|
|
const getPointOffSide = (side, point) => {
|
|
return {
|
|
left: [point[0] - dist, point[1]],
|
|
right: [point[0] + dist, point[1]],
|
|
top: [point[0], point[1] - dist],
|
|
bottom: [point[0], point[1] + dist],
|
|
}[side];
|
|
};
|
|
const p1 = getPointOffSide(sourceSide, sourcePoint);
|
|
const p2 = getPointOffSide(targetSide, targetPoint);
|
|
const p3 = freeJoin(p1, p2, bbox);
|
|
controlPoints.push(p1, p3, p2);
|
|
}
|
|
}
|
|
return controlPoints;
|
|
}
|
|
/**
|
|
* <zh/> 获取子图内的所有边,并按照内部边和外部边分组
|
|
*
|
|
* <en/> Get all the edges in the subgraph and group them into internal and external edges
|
|
* @param ids - <zh/> 节点 ID 数组 | <en/> Node ID array
|
|
* @param getRelatedEdges - <zh/> 获取节点邻边 | <en/> Get node edges
|
|
* @returns <zh/> 子图边 | <en/> Subgraph edges
|
|
*/
|
|
export function getSubgraphRelatedEdges(ids, getRelatedEdges) {
|
|
const edges = new Set();
|
|
const internal = new Set();
|
|
const external = new Set();
|
|
ids.forEach((id) => {
|
|
const relatedEdges = getRelatedEdges(id);
|
|
relatedEdges.forEach((edge) => {
|
|
edges.add(edge);
|
|
if (ids.includes(edge.source) && ids.includes(edge.target))
|
|
internal.add(edge);
|
|
else
|
|
external.add(edge);
|
|
});
|
|
});
|
|
return { edges: Array.from(edges), internal: Array.from(internal), external: Array.from(external) };
|
|
}
|
|
/**
|
|
* <zh/> 获取边的实际连接节点
|
|
*
|
|
* <en/> Get the actual connected object of the edge
|
|
* @param node - <zh/> 逻辑连接节点数据 | <en/> Logical connection node data
|
|
* @param getParentData - <zh/> 获取父节点数据 | <en/> Get parent node data
|
|
* @returns <zh/> 实际连接节点数据 | <en/> Actual connected node data
|
|
*/
|
|
export function findActualConnectNodeData(node, getParentData) {
|
|
const path = [];
|
|
let current = node;
|
|
while (current) {
|
|
path.push(current);
|
|
const parent = getParentData(idOf(current));
|
|
if (parent)
|
|
current = parent;
|
|
else
|
|
break;
|
|
}
|
|
if (path.some((n) => { var _a; return (_a = n.style) === null || _a === void 0 ? void 0 : _a.collapsed; })) {
|
|
const index = path.reverse().findIndex(isCollapsed);
|
|
return path[index] || path.at(-1);
|
|
}
|
|
return node;
|
|
}
|
|
/**
|
|
* <zh/> 获取箭头大小,若用户未指定,则根据线宽自动计算
|
|
*
|
|
* <en/> Get the size of the arrow
|
|
* @param lineWidth - <zh/> 箭头所在边的线宽 | <en/> The line width of the edge where the arrow is located
|
|
* @param size - <zh/> 自定义箭头大小 | <en/> Custom arrow size
|
|
* @returns <zh/> 箭头大小 | <en/> Arrow size
|
|
*/
|
|
export function getArrowSize(lineWidth, size) {
|
|
if (size)
|
|
return size;
|
|
if (lineWidth < 4)
|
|
return 10;
|
|
if (lineWidth === 4)
|
|
return 12;
|
|
return lineWidth * 2.5;
|
|
}
|
|
//# sourceMappingURL=edge.js.map
|