From 375a0d22baf0ecddd4f3de95f31e9b0f239a98a4 Mon Sep 17 00:00:00 2001 From: lxfu1 <954055752@qq.com> Date: Tue, 1 Jul 2025 15:54:28 +0800 Subject: [PATCH 1/4] fix: Fix the infinite loop caused by equalizeSegments in special cases --- src/path/util/equalize-segments.ts | 44 +++++++++++++++++------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/path/util/equalize-segments.ts b/src/path/util/equalize-segments.ts index 181686a..d0ddce6 100644 --- a/src/path/util/equalize-segments.ts +++ b/src/path/util/equalize-segments.ts @@ -4,6 +4,8 @@ import { segmentCubicFactory } from './segment-cubic-factory'; type SplitArray = [number, number, number, number, number, number, number, number, number]; +const MAX_RECURSION_DEPTH = 50; + function splitCubic(pts: SplitArray, t = 0.5): [CurveArray, CurveArray] { const p0 = pts.slice(0, 2) as [number, number]; const p1 = pts.slice(2, 4) as [number, number]; @@ -32,17 +34,17 @@ function getCurveArray(segments: PathArray) { // @ts-ignore const curveLength = i ? segmentCubicFactory( - segmentData[0], - segmentData[1], - segmentData[2], - segmentData[3], - segmentData[4], - segmentData[5], - segmentData[6], - segmentData[7], - segmentData[8], - { bbox: false }, - ).length + segmentData[0], + segmentData[1], + segmentData[2], + segmentData[3], + segmentData[4], + segmentData[5], + segmentData[6], + segmentData[7], + segmentData[8], + { bbox: false }, + ).length : 0; let subsegs; @@ -61,7 +63,11 @@ function getCurveArray(segments: PathArray) { }); } -export function equalizeSegments(path1: PathArray, path2: PathArray, TL?: number): CurveArray[] { +export function equalizeSegments(path1: PathArray, path2: PathArray, TL?: number, depth = 0): CurveArray[] { + if (depth > MAX_RECURSION_DEPTH) { + console.warn('Maximum recursion depth reached in equalizeSegments'); + return [path1, path2] as CurveArray[]; + } const c1 = getCurveArray(path1); const c2 = getCurveArray(path2); const L1 = c1.length; @@ -79,13 +85,13 @@ export function equalizeSegments(path1: PathArray, path2: PathArray, TL?: number x.l === tl ? x.map((y) => y.s) : x - .map((y, j) => { - canSplit = j && dif[i] && y.l >= mm[i]; - dif[i] -= canSplit ? 1 : 0; - return canSplit ? y.ss : [y.s]; - }) - .flat(), + .map((y, j) => { + canSplit = j && dif[i] && y.l >= mm[i]; + dif[i] -= canSplit ? 1 : 0; + return canSplit ? y.ss : [y.s]; + }) + .flat(), ) as CurveArray[]; - return result[0].length === result[1].length ? result : equalizeSegments(result[0], result[1], tl); + return result[0].length === result[1].length ? result : equalizeSegments(result[0], result[1], tl, depth + 1); } From 7a70a035c516a4d57c76cd6d168c6f05ca8deebc Mon Sep 17 00:00:00 2001 From: lxfu1 <954055752@qq.com> Date: Tue, 1 Jul 2025 16:13:16 +0800 Subject: [PATCH 2/4] fix: code style --- src/path/util/equalize-segments.ts | 34 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/path/util/equalize-segments.ts b/src/path/util/equalize-segments.ts index d0ddce6..2283dac 100644 --- a/src/path/util/equalize-segments.ts +++ b/src/path/util/equalize-segments.ts @@ -34,17 +34,17 @@ function getCurveArray(segments: PathArray) { // @ts-ignore const curveLength = i ? segmentCubicFactory( - segmentData[0], - segmentData[1], - segmentData[2], - segmentData[3], - segmentData[4], - segmentData[5], - segmentData[6], - segmentData[7], - segmentData[8], - { bbox: false }, - ).length + segmentData[0], + segmentData[1], + segmentData[2], + segmentData[3], + segmentData[4], + segmentData[5], + segmentData[6], + segmentData[7], + segmentData[8], + { bbox: false }, + ).length : 0; let subsegs; @@ -85,12 +85,12 @@ export function equalizeSegments(path1: PathArray, path2: PathArray, TL?: number x.l === tl ? x.map((y) => y.s) : x - .map((y, j) => { - canSplit = j && dif[i] && y.l >= mm[i]; - dif[i] -= canSplit ? 1 : 0; - return canSplit ? y.ss : [y.s]; - }) - .flat(), + .map((y, j) => { + canSplit = j && dif[i] && y.l >= mm[i]; + dif[i] -= canSplit ? 1 : 0; + return canSplit ? y.ss : [y.s]; + }) + .flat(), ) as CurveArray[]; return result[0].length === result[1].length ? result : equalizeSegments(result[0], result[1], tl, depth + 1); From 55db19a14a94fbeb16b5ea63c4c74e1a68591020 Mon Sep 17 00:00:00 2001 From: lxfu1 <954055752@qq.com> Date: Tue, 1 Jul 2025 16:34:37 +0800 Subject: [PATCH 3/4] ci: add tests --- __tests__/unit/path/equalize-segments.spec.ts | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/__tests__/unit/path/equalize-segments.spec.ts b/__tests__/unit/path/equalize-segments.spec.ts index 1d9b758..7036e40 100644 --- a/__tests__/unit/path/equalize-segments.spec.ts +++ b/__tests__/unit/path/equalize-segments.spec.ts @@ -98,4 +98,64 @@ describe('equalize segments', () => { ], ]); }); + it('should not recurse infinitely if segments cannot be split', () => { + const path1: PathArray = [ + ['M', 0, 0], + ['L', 1, 1], // 非常短的线段,不满足 split 条件 + ]; + const path2: PathArray = [ + ['M', 0, 0], + ['L', 10, 10], + ['L', 20, 20], + ['L', 30, 30], + ]; + + const result = equalizeSegments(path1, path2); + // 不是一定相等,因为可能无法拆分,重点是不会死循环 + expect(Array.isArray(result)).toBe(true); + expect(result.length).toBe(2); + }); + it('should split cubic bezier curves correctly', () => { + const path1: PathArray = [ + ['M', 0, 0], + ['C', 30, 30, 60, 30, 100, 0], + ]; + const path2: PathArray = [ + ['M', 0, 0], + ['C', 20, 20, 40, 20, 60, 0], + ['C', 70, -20, 90, -20, 100, 0], + ]; + + const result = equalizeSegments(path1, path2); + expect(result[0].length).toBe(result[1].length); + }); + it('should equalizeSegments for complex multi-segment paths', () => { + const path1: PathArray = [ + ['M', 0, 0], + ['L', 50, 0], + ['C', 60, 10, 70, 10, 80, 0], + ]; + const path2: PathArray = [ + ['M', 0, 0], + ['L', 20, 0], + ['L', 40, 0], + ['L', 60, 0], + ['C', 65, 5, 75, 5, 80, 0], + ]; + + const result = equalizeSegments(path1, path2); + expect(result[0].length).toBe(result[1].length); + }); + it('should terminate recursion at max depth', () => { + const path1: PathArray = [ + ['M', 0, 0], + ['L', 1, 1], + ]; + // @ts-ignore + const path2: PathArray = Array.from({ length: 20 }, (_, i) => ['L', i, i + 1]) as PathArray; + path2.unshift(['M', 0, 0]); + + const result = equalizeSegments(path1, path2); + expect(result.length).toBe(2); + }); }); From 8005850a36fb6d204fd8ddbb99159a707734ea36 Mon Sep 17 00:00:00 2001 From: lxfu1 <954055752@qq.com> Date: Tue, 1 Jul 2025 16:49:55 +0800 Subject: [PATCH 4/4] chore: update version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eddeec6..2eb4758 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@antv/util", - "version": "3.3.10", + "version": "3.3.11", "license": "MIT", "sideEffects": false, "main": "lib/index.js",