Skip to content

Commit 63956af

Browse files
committed
feat: 折线增加路径正交矫正逻辑
1 parent 904f799 commit 63956af

1 file changed

Lines changed: 133 additions & 15 deletions

File tree

packages/core/src/model/edge/PolylineEdgeModel.ts

Lines changed: 133 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ export class PolylineEdgeModel extends BaseEdgeModel {
4242
? providedOffset
4343
: this.getDefaultOffset()
4444
if (data.pointsList) {
45-
this.pointsList = data.pointsList
45+
const corrected = this.orthogonalizePath(data.pointsList)
46+
;(data as any).pointsList = corrected
47+
this.pointsList = corrected
4648
}
4749
super.initEdgeData(data)
4850
}
@@ -55,6 +57,120 @@ export class PolylineEdgeModel extends BaseEdgeModel {
5557
}
5658
}
5759

60+
orthogonalizePath(points: Point[]): Point[] {
61+
// 输入非法或不足两点时直接返回副本
62+
if (!Array.isArray(points) || points.length < 2) {
63+
return points
64+
}
65+
// pushUnique: 向数组中添加唯一点,避免重复
66+
const pushUnique = (arr: Point[], p: Point) => {
67+
const last = arr[arr.length - 1]
68+
if (!last || last.x !== p.x || last.y !== p.y) {
69+
arr.push({ x: p.x, y: p.y })
70+
}
71+
}
72+
// isAxisAligned: 检查两点是否在同一条轴上
73+
const isAxisAligned = (a: Point, b: Point) => a.x === b.x || a.y === b.y
74+
// manhattanDistance: 计算两点在曼哈顿距离上的距离
75+
const manhattanDistance = (a: Point, b: Point) =>
76+
Math.abs(a.x - b.x) + Math.abs(a.y - b.y)
77+
78+
// 1) 生成严格正交路径,尽量延续前一段方向以减少折点
79+
const orthogonal: Point[] = []
80+
pushUnique(orthogonal, points[0])
81+
// previousDirection: 记录前一段的方向,用于判断当前段的PreferredCorner
82+
let previousDirection: SegmentDirection | undefined
83+
// 遍历所有点对,生成正交路径
84+
for (let i = 0; i < points.length - 1; i++) {
85+
const current = orthogonal[orthogonal.length - 1]
86+
const next = points[i + 1]
87+
if (!current || !next) continue
88+
89+
if (isAxisAligned(current, next)) {
90+
pushUnique(orthogonal, next)
91+
previousDirection =
92+
current.x === next.x
93+
? SegmentDirection.VERTICAL
94+
: SegmentDirection.HORIZONTAL
95+
continue
96+
}
97+
98+
const cornerHV: Point = { x: next.x, y: current.y }
99+
const cornerVH: Point = { x: current.x, y: next.y }
100+
101+
// 根据前一段的方向,优先选择能延续该方向的拐角点,以减少折点数量;
102+
// 若前一段为垂直方向,则优先选择垂直-水平拐角(cornerVH);
103+
// 若前一段为水平方向,则优先选择水平-垂直拐角(cornerHV);
104+
// 若前一段无方向(初始情况),则比较两个拐角的曼哈顿距离,选更近者。
105+
const preferredCorner =
106+
previousDirection === SegmentDirection.VERTICAL
107+
? cornerVH
108+
: previousDirection === SegmentDirection.HORIZONTAL
109+
? cornerHV
110+
: manhattanDistance(current, cornerHV) <=
111+
manhattanDistance(current, cornerVH)
112+
? cornerHV
113+
: cornerVH
114+
115+
if (preferredCorner.x !== current.x || preferredCorner.y !== current.y) {
116+
pushUnique(orthogonal, preferredCorner)
117+
}
118+
pushUnique(orthogonal, next)
119+
120+
const a = orthogonal[orthogonal.length - 2]
121+
const b = orthogonal[orthogonal.length - 1]
122+
previousDirection =
123+
a && b
124+
? a.x === b.x
125+
? SegmentDirection.VERTICAL
126+
: SegmentDirection.HORIZONTAL
127+
: previousDirection
128+
}
129+
130+
// 2) 去除冗余共线中间点
131+
const simplified: Point[] = []
132+
for (let i = 0; i < orthogonal.length; i++) {
133+
const prev = orthogonal[i - 1]
134+
const curr = orthogonal[i]
135+
const next = orthogonal[i + 1]
136+
// 如果当前点与前一个点和后一个点在同一条水平线或垂直线上,则跳过该点,去除冗余的共线中间点
137+
if (
138+
prev &&
139+
curr &&
140+
next &&
141+
((prev.x === curr.x && curr.x === next.x) || // 水平共线
142+
(prev.y === curr.y && curr.y === next.y)) // 垂直共线
143+
) {
144+
continue
145+
}
146+
pushUnique(simplified, curr)
147+
}
148+
149+
// 3) 保留原始起点与终点位置
150+
if (simplified.length >= 2) {
151+
simplified[0] = { x: points[0].x, y: points[0].y }
152+
simplified[simplified.length - 1] = {
153+
x: points[points.length - 1].x,
154+
y: points[points.length - 1].y,
155+
}
156+
}
157+
158+
// 4) 结果校验:任意相邻段都必须为水平/垂直;失败则退化为起止两点
159+
const isOrthogonal =
160+
simplified.length < 2 ||
161+
simplified.every((_, idx, arr) => {
162+
if (idx === 0) return true
163+
return isAxisAligned(arr[idx - 1], arr[idx])
164+
})
165+
166+
return isOrthogonal
167+
? simplified
168+
: [
169+
{ x: points[0].x, y: points[0].y },
170+
{ x: points[points.length - 1].x, y: points[points.length - 1].y },
171+
]
172+
}
173+
58174
/**
59175
* 计算默认 offset:箭头与折线重叠长度 + 5
60176
* 重叠长度采用箭头样式中的 offset(沿边方向的长度)
@@ -344,7 +460,7 @@ export class PolylineEdgeModel extends BaseEdgeModel {
344460
}
345461

346462
updatePath(pointList: Point[]) {
347-
this.pointsList = pointList
463+
this.pointsList = this.orthogonalizePath(pointList)
348464
this.points = this.getPath(this.pointsList)
349465
}
350466

@@ -387,7 +503,7 @@ export class PolylineEdgeModel extends BaseEdgeModel {
387503
this.targetNode,
388504
this.offset || 0,
389505
)
390-
this.pointsList = pointsList
506+
this.pointsList = this.orthogonalizePath(pointsList)
391507
this.points = pointsList.map((point) => `${point.x},${point.y}`).join(' ')
392508
}
393509

@@ -676,18 +792,20 @@ export class PolylineEdgeModel extends BaseEdgeModel {
676792
sourceNode: BaseNodeModel
677793
targetNode: BaseNodeModel
678794
}) {
679-
this.pointsList = getPolylinePoints(
680-
{
681-
x: startPoint.x,
682-
y: startPoint.y,
683-
},
684-
{
685-
x: endPoint.x,
686-
y: endPoint.y,
687-
},
688-
sourceNode,
689-
targetNode,
690-
this.offset || 0,
795+
this.pointsList = this.orthogonalizePath(
796+
getPolylinePoints(
797+
{
798+
x: startPoint.x,
799+
y: startPoint.y,
800+
},
801+
{
802+
x: endPoint.x,
803+
y: endPoint.y,
804+
},
805+
sourceNode,
806+
targetNode,
807+
this.offset || 0,
808+
),
691809
)
692810

693811
this.initPoints()

0 commit comments

Comments
 (0)