Skip to content

Commit 426c79b

Browse files
authored
fix: Fix the infinite loop caused by equalizeSegments in special cases (#129)
* fix: Fix the infinite loop caused by equalizeSegments in special cases * fix: code style * ci: add tests * chore: update version
1 parent 633e97d commit 426c79b

3 files changed

Lines changed: 69 additions & 3 deletions

File tree

__tests__/unit/path/equalize-segments.spec.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,64 @@ describe('equalize segments', () => {
9898
],
9999
]);
100100
});
101+
it('should not recurse infinitely if segments cannot be split', () => {
102+
const path1: PathArray = [
103+
['M', 0, 0],
104+
['L', 1, 1], // 非常短的线段,不满足 split 条件
105+
];
106+
const path2: PathArray = [
107+
['M', 0, 0],
108+
['L', 10, 10],
109+
['L', 20, 20],
110+
['L', 30, 30],
111+
];
112+
113+
const result = equalizeSegments(path1, path2);
114+
// 不是一定相等,因为可能无法拆分,重点是不会死循环
115+
expect(Array.isArray(result)).toBe(true);
116+
expect(result.length).toBe(2);
117+
});
118+
it('should split cubic bezier curves correctly', () => {
119+
const path1: PathArray = [
120+
['M', 0, 0],
121+
['C', 30, 30, 60, 30, 100, 0],
122+
];
123+
const path2: PathArray = [
124+
['M', 0, 0],
125+
['C', 20, 20, 40, 20, 60, 0],
126+
['C', 70, -20, 90, -20, 100, 0],
127+
];
128+
129+
const result = equalizeSegments(path1, path2);
130+
expect(result[0].length).toBe(result[1].length);
131+
});
132+
it('should equalizeSegments for complex multi-segment paths', () => {
133+
const path1: PathArray = [
134+
['M', 0, 0],
135+
['L', 50, 0],
136+
['C', 60, 10, 70, 10, 80, 0],
137+
];
138+
const path2: PathArray = [
139+
['M', 0, 0],
140+
['L', 20, 0],
141+
['L', 40, 0],
142+
['L', 60, 0],
143+
['C', 65, 5, 75, 5, 80, 0],
144+
];
145+
146+
const result = equalizeSegments(path1, path2);
147+
expect(result[0].length).toBe(result[1].length);
148+
});
149+
it('should terminate recursion at max depth', () => {
150+
const path1: PathArray = [
151+
['M', 0, 0],
152+
['L', 1, 1],
153+
];
154+
// @ts-ignore
155+
const path2: PathArray = Array.from({ length: 20 }, (_, i) => ['L', i, i + 1]) as PathArray;
156+
path2.unshift(['M', 0, 0]);
157+
158+
const result = equalizeSegments(path1, path2);
159+
expect(result.length).toBe(2);
160+
});
101161
});

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@antv/util",
3-
"version": "3.3.10",
3+
"version": "3.3.11",
44
"license": "MIT",
55
"sideEffects": false,
66
"main": "lib/index.js",

src/path/util/equalize-segments.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { segmentCubicFactory } from './segment-cubic-factory';
44

55
type SplitArray = [number, number, number, number, number, number, number, number, number];
66

7+
const MAX_RECURSION_DEPTH = 50;
8+
79
function splitCubic(pts: SplitArray, t = 0.5): [CurveArray, CurveArray] {
810
const p0 = pts.slice(0, 2) as [number, number];
911
const p1 = pts.slice(2, 4) as [number, number];
@@ -61,7 +63,11 @@ function getCurveArray(segments: PathArray) {
6163
});
6264
}
6365

64-
export function equalizeSegments(path1: PathArray, path2: PathArray, TL?: number): CurveArray[] {
66+
export function equalizeSegments(path1: PathArray, path2: PathArray, TL?: number, depth = 0): CurveArray[] {
67+
if (depth > MAX_RECURSION_DEPTH) {
68+
console.warn('Maximum recursion depth reached in equalizeSegments');
69+
return [path1, path2] as CurveArray[];
70+
}
6571
const c1 = getCurveArray(path1);
6672
const c2 = getCurveArray(path2);
6773
const L1 = c1.length;
@@ -87,5 +93,5 @@ export function equalizeSegments(path1: PathArray, path2: PathArray, TL?: number
8793
.flat(),
8894
) as CurveArray[];
8995

90-
return result[0].length === result[1].length ? result : equalizeSegments(result[0], result[1], tl);
96+
return result[0].length === result[1].length ? result : equalizeSegments(result[0], result[1], tl, depth + 1);
9197
}

0 commit comments

Comments
 (0)