@@ -34,13 +34,154 @@ export class PolylineEdgeModel extends BaseEdgeModel {
3434 @observable dbClickPosition ?: Point
3535
3636 initEdgeData ( data : LogicFlow . EdgeConfig ) : void {
37- this . offset = get ( data , 'properties.offset' , 30 )
37+ const providedOffset = get ( data , 'properties.offset' )
38+ // 当用户未传入 offset 时,按“箭头与折线重叠长度 + 5”作为默认值
39+ // 其中“重叠长度”采用箭头样式中的 offset(沿边方向的长度)
40+ this . offset =
41+ typeof providedOffset === 'number'
42+ ? providedOffset
43+ : this . getDefaultOffset ( )
3844 if ( data . pointsList ) {
39- this . pointsList = data . pointsList
45+ const corrected = this . orthogonalizePath ( data . pointsList )
46+ ; ( data as any ) . pointsList = corrected
47+ this . pointsList = corrected
4048 }
4149 super . initEdgeData ( data )
4250 }
4351
52+ setAttributes ( ) {
53+ const { offset : newOffset } = this . properties
54+ if ( newOffset && newOffset !== this . offset ) {
55+ this . offset = newOffset
56+ this . updatePoints ( )
57+ }
58+ }
59+
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+
174+ /**
175+ * 计算默认 offset:箭头与折线重叠长度 + 5
176+ * 重叠长度采用箭头样式中的 offset(沿边方向的长度)
177+ */
178+ private getDefaultOffset ( ) : number {
179+ const arrowStyle = this . getArrowStyle ( )
180+ const arrowOverlap =
181+ typeof arrowStyle . offset === 'number' ? arrowStyle . offset : 0
182+ return arrowOverlap + 5
183+ }
184+
44185 getEdgeStyle ( ) {
45186 const { polyline } = this . graphModel . theme
46187 const style = super . getEdgeStyle ( )
@@ -319,7 +460,7 @@ export class PolylineEdgeModel extends BaseEdgeModel {
319460 }
320461
321462 updatePath ( pointList : Point [ ] ) {
322- this . pointsList = pointList
463+ this . pointsList = this . orthogonalizePath ( pointList )
323464 this . points = this . getPath ( this . pointsList )
324465 }
325466
@@ -362,7 +503,7 @@ export class PolylineEdgeModel extends BaseEdgeModel {
362503 this . targetNode ,
363504 this . offset || 0 ,
364505 )
365- this . pointsList = pointsList
506+ this . pointsList = this . orthogonalizePath ( pointsList )
366507 this . points = pointsList . map ( ( point ) => `${ point . x } ,${ point . y } ` ) . join ( ' ' )
367508 }
368509
@@ -651,18 +792,20 @@ export class PolylineEdgeModel extends BaseEdgeModel {
651792 sourceNode : BaseNodeModel
652793 targetNode : BaseNodeModel
653794 } ) {
654- this . pointsList = getPolylinePoints (
655- {
656- x : startPoint . x ,
657- y : startPoint . y ,
658- } ,
659- {
660- x : endPoint . x ,
661- y : endPoint . y ,
662- } ,
663- sourceNode ,
664- targetNode ,
665- 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+ ) ,
666809 )
667810
668811 this . initPoints ( )
0 commit comments