JointJS中如何实现可绕开障碍物的平滑曲线连接链路
JointJS平滑避障链路实现方案
核心优化思路
你遇到的问题本质是原生路由输出的点位结构和smooth连接器的计算逻辑不匹配,需要同时对路由和连接器做自定义改造:
一、自定义路由改造
- 先调用原生metro路由生成基础避障路径点,再使用Ramer-Douglas-Peucker算法对路径点做抽稀处理,过滤掉偏差小于阈值的冗余拐点,仅保留核心转弯点位,从根源减少曲线过度弯曲的问题,抽稀阈值可根据画布精度设置为10~20px。
- 路由计算时额外输出起始点出边方向、结束点入边方向参数,和路径点一起传递给连接器,用于固定首尾段曲线走向。
二、自定义连接器改造
- 放弃原生smooth连接器的自动控制点计算逻辑,采用分段三次贝塞尔曲线生成路径。相邻两个拐点之间的控制点偏移距离统一设置为两点间距的1/3,最大不超过30px,保证全链路曲率均匀。
- 用路由输出的首尾方向强制约束首尾段控制点坐标:比如起始点端口朝右,则首段第一个控制点的Y坐标和起始点完全一致,仅X坐标偏移对应距离;结束点端口朝下,则尾段第二个控制点的X坐标和结束点完全一致,仅Y坐标偏移对应距离,彻底解决首尾异常渲染的问题。
- 可选增加兜底校验:曲线生成后对路径做等距采样,检测采样点是否和障碍物碰撞,若存在碰撞则通知路由在碰撞位置新增偏移拐点,重新生成曲线直到完全避障。
核心代码示例
// 自定义平滑避障连接器核心实现 function customSmoothConnector(sourcePoint, targetPoint, routePoints, options) { const allPoints = [sourcePoint, ...routePoints, targetPoint]; let pathStr = `M ${sourcePoint.x} ${sourcePoint.y}`; const maxControlOffset = 30; for (let i = 1; i < allPoints.length; i++) { const prevPoint = allPoints[i - 1]; const currPoint = allPoints[i]; const segmentLength = prevPoint.distance(currPoint); const controlOffset = Math.min(maxControlOffset, segmentLength / 3); let cp1X, cp1Y, cp2X, cp2Y; if (i === 1) { // 首段对齐出边方向(示例为水平向右,可根据路由返回的方向动态调整) cp1X = prevPoint.x + controlOffset; cp1Y = prevPoint.y; cp2X = currPoint.x - controlOffset; cp2Y = currPoint.y; } else if (i === allPoints.length - 1) { // 尾段对齐入边方向(示例为水平向左,可根据路由返回的方向动态调整) cp1X = prevPoint.x + controlOffset; cp1Y = prevPoint.y; cp2X = currPoint.x - controlOffset; cp2Y = currPoint.y; } else { // 中间段均匀设置控制点 cp1X = prevPoint.x + controlOffset; cp1Y = prevPoint.y; cp2X = currPoint.x - controlOffset; cp2Y = currPoint.y; } pathStr += ` C ${cp1X} ${cp1Y} ${cp2X} ${cp2Y} ${currPoint.x} ${currPoint.y}`; } return pathStr; }
内容的提问来源于stack exchange,提问作者Dov Rosenberg
相关产品推荐
相关产品推荐

