From 4abbff5ff9a7075e07807599c16a48465cf7cf58 Mon Sep 17 00:00:00 2001 From: Kendell R Date: Tue, 19 Dec 2023 21:48:17 -0500 Subject: [PATCH 1/8] feat(convertPathData): convert c to q --- docs/03-plugins/convert-path-data.mdx | 3 +++ plugins/convertPathData.js | 32 +++++++++++++++++++++++++++ plugins/plugins-types.d.ts | 1 + 3 files changed, 36 insertions(+) diff --git a/docs/03-plugins/convert-path-data.mdx b/docs/03-plugins/convert-path-data.mdx index 23ca9e7e0..79a8baa3b 100644 --- a/docs/03-plugins/convert-path-data.mdx +++ b/docs/03-plugins/convert-path-data.mdx @@ -15,6 +15,9 @@ svgo: straightCurves: description: If to convert curve commands that are effectively straight lines to line commands. default: true + convertToQ: + description: If to convert cubic beziers to quadratic beziers when they effectively are. + default: true lineShorthands: description: If to convert regular lines to an explicit horizontal or vertical line where possible. default: true diff --git a/plugins/convertPathData.js b/plugins/convertPathData.js index b4ef21147..6698f1aa4 100644 --- a/plugins/convertPathData.js +++ b/plugins/convertPathData.js @@ -45,6 +45,7 @@ let arcTolerance; * tolerance: number, * }, * straightCurves: boolean, + * convertToQ: boolean, * lineShorthands: boolean, * convertToZ: boolean, * curveSmoothShorthands: boolean, @@ -95,6 +96,7 @@ exports.fn = (root, params) => { tolerance: 0.5, // percentage of radius }, straightCurves = true, + convertToQ = true, lineShorthands = true, convertToZ = true, curveSmoothShorthands = true, @@ -117,6 +119,7 @@ exports.fn = (root, params) => { applyTransformsStroked, makeArcs, straightCurves, + convertToQ, lineShorthands, convertToZ, curveSmoothShorthands, @@ -665,6 +668,35 @@ function filters( } } + // degree-lower c to q when possible + // m 0 12 C 4 4 8 4 12 12 → M 0 12 Q 6 0 12 12 + if (params.convertToQ && command == 'c') { + const x1 = + // @ts-ignore + 1.5 * (item.base[0] + data[0]) - 0.5 * item.base[0]; + const x2 = + // @ts-ignore + 1.5 * (item.base[0] + data[2]) - 0.5 * (item.base[0] + data[4]); + if (Math.abs(x1 - x2) < error) { + const y1 = + // @ts-ignore + 1.5 * (item.base[1] + data[1]) - 0.5 * item.base[1]; + const y2 = + // @ts-ignore + 1.5 * (item.base[1] + data[3]) - 0.5 * (item.base[1] + data[5]); + if (Math.abs(y1 - y2) < error) { + command = 'q'; + // @ts-ignore + data[0] = (x1 + x2) / 2 - item.base[0]; + // @ts-ignore + data[1] = (y1 + y2) / 2 - item.base[1]; + data.splice(2, 2); + + if (next && next.command == 's') makeLonghand(next, data); // fix up next curve + } + } + } + // horizontal and vertical line shorthands // l 50 0 → h 50 // l 0 50 → v 50 diff --git a/plugins/plugins-types.d.ts b/plugins/plugins-types.d.ts index ff25f68cd..b7cd1b065 100644 --- a/plugins/plugins-types.d.ts +++ b/plugins/plugins-types.d.ts @@ -41,6 +41,7 @@ type DefaultPlugins = { tolerance: number; }; straightCurves?: boolean; + convertToQ?: boolean; lineShorthands?: boolean; convertToZ?: boolean; curveSmoothShorthands?: boolean; From 436eaabc8ba4cc62ba354ecfb4e680de9bafa07e Mon Sep 17 00:00:00 2001 From: Kendell R Date: Tue, 19 Dec 2023 22:05:36 -0500 Subject: [PATCH 2/8] better size reduction --- plugins/convertPathData.js | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/plugins/convertPathData.js b/plugins/convertPathData.js index 6698f1aa4..8050878ba 100644 --- a/plugins/convertPathData.js +++ b/plugins/convertPathData.js @@ -677,22 +677,31 @@ function filters( const x2 = // @ts-ignore 1.5 * (item.base[0] + data[2]) - 0.5 * (item.base[0] + data[4]); - if (Math.abs(x1 - x2) < error) { + if (Math.abs(x1 - x2) < error * 2) { const y1 = // @ts-ignore 1.5 * (item.base[1] + data[1]) - 0.5 * item.base[1]; const y2 = // @ts-ignore 1.5 * (item.base[1] + data[3]) - 0.5 * (item.base[1] + data[5]); - if (Math.abs(y1 - y2) < error) { - command = 'q'; - // @ts-ignore - data[0] = (x1 + x2) / 2 - item.base[0]; - // @ts-ignore - data[1] = (y1 + y2) / 2 - item.base[1]; - data.splice(2, 2); - - if (next && next.command == 's') makeLonghand(next, data); // fix up next curve + if (Math.abs(y1 - y2) < error * 2) { + const newData = data.slice(); + newData.splice( + 0, + 4, + // @ts-ignore + (x1 + x2) / 2 - item.base[0], + // @ts-ignore + (y1 + y2) / 2 - item.base[1], + ); + roundData(newData); + const originalLength = cleanupOutData(data, params).length, + newLength = cleanupOutData(newData, params).length; + if (newLength < originalLength) { + command = 'q'; + data = newData; + if (next && next.command == 's') makeLonghand(next, data); // fix up next curve + } } } } From 262f2d70554f187f54e3957eef5412d27cfdf9c2 Mon Sep 17 00:00:00 2001 From: Kendell R Date: Tue, 19 Dec 2023 22:11:43 -0500 Subject: [PATCH 3/8] get tests fixed --- test/plugins/convertPathData.16.svg | 2 +- test/plugins/convertPathData.30.svg | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 test/plugins/convertPathData.30.svg diff --git a/test/plugins/convertPathData.16.svg b/test/plugins/convertPathData.16.svg index 4fcee12fa..179d0f00c 100644 --- a/test/plugins/convertPathData.16.svg +++ b/test/plugins/convertPathData.16.svg @@ -7,7 +7,7 @@ - + @@@ diff --git a/test/plugins/convertPathData.30.svg b/test/plugins/convertPathData.30.svg new file mode 100644 index 000000000..3cff256cb --- /dev/null +++ b/test/plugins/convertPathData.30.svg @@ -0,0 +1,13 @@ +Should convert C to Q + +=== + + + + + +@@@ + + + + \ No newline at end of file From 3108b9372707783bdaa5eaa9c687226c66384333 Mon Sep 17 00:00:00 2001 From: Kendell R Date: Tue, 19 Dec 2023 22:14:39 -0500 Subject: [PATCH 4/8] format test --- test/plugins/convertPathData.30.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/plugins/convertPathData.30.svg b/test/plugins/convertPathData.30.svg index 3cff256cb..432c2878c 100644 --- a/test/plugins/convertPathData.30.svg +++ b/test/plugins/convertPathData.30.svg @@ -10,4 +10,4 @@ Should convert C to Q - \ No newline at end of file + From 1f1843bcae516f95c585f4c51a7af8ccdb9558c8 Mon Sep 17 00:00:00 2001 From: Kendell R Date: Wed, 20 Dec 2023 10:51:41 -0500 Subject: [PATCH 5/8] okay i did some math and it turns out it can be 4x the error --- plugins/convertPathData.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/convertPathData.js b/plugins/convertPathData.js index 8050878ba..b86fbbe05 100644 --- a/plugins/convertPathData.js +++ b/plugins/convertPathData.js @@ -677,14 +677,14 @@ function filters( const x2 = // @ts-ignore 1.5 * (item.base[0] + data[2]) - 0.5 * (item.base[0] + data[4]); - if (Math.abs(x1 - x2) < error * 2) { + if (Math.abs(x1 - x2) < error * 4) { const y1 = // @ts-ignore 1.5 * (item.base[1] + data[1]) - 0.5 * item.base[1]; const y2 = // @ts-ignore 1.5 * (item.base[1] + data[3]) - 0.5 * (item.base[1] + data[5]); - if (Math.abs(y1 - y2) < error * 2) { + if (Math.abs(y1 - y2) < error * 4) { const newData = data.slice(); newData.splice( 0, From 3c7561274c79fa91018fbffe9e95c74dddb61840 Mon Sep 17 00:00:00 2001 From: Kendell R Date: Wed, 20 Dec 2023 10:57:47 -0500 Subject: [PATCH 6/8] update another test dont worry that it now looks different, it's still under the error so its really just doing what svgo is supposed to do --- test/plugins/convertPathData.16.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/plugins/convertPathData.16.svg b/test/plugins/convertPathData.16.svg index 179d0f00c..29302d876 100644 --- a/test/plugins/convertPathData.16.svg +++ b/test/plugins/convertPathData.16.svg @@ -7,7 +7,7 @@ - + @@@ From 0391c4ca2d8900cbb1e429bb763267fd2df80241 Mon Sep 17 00:00:00 2001 From: Kendell R Date: Mon, 1 Jan 2024 12:29:59 -0800 Subject: [PATCH 7/8] Update math per review Co-authored-by: Seth Falco --- plugins/convertPathData.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/convertPathData.js b/plugins/convertPathData.js index 6d06983af..feab1724b 100644 --- a/plugins/convertPathData.js +++ b/plugins/convertPathData.js @@ -705,26 +705,26 @@ function filters( if (params.convertToQ && command == 'c') { const x1 = // @ts-ignore - 1.5 * (item.base[0] + data[0]) - 0.5 * item.base[0]; + 0.75 * (item.base[0] + data[0]) - 0.25 * item.base[0]; const x2 = // @ts-ignore - 1.5 * (item.base[0] + data[2]) - 0.5 * (item.base[0] + data[4]); - if (Math.abs(x1 - x2) < error * 4) { + 0.75 * (item.base[0] + data[2]) - 0.25 * (item.base[0] + data[4]); + if (Math.abs(x1 - x2) < error * 2) { const y1 = // @ts-ignore - 1.5 * (item.base[1] + data[1]) - 0.5 * item.base[1]; + 0.75 * (item.base[1] + data[1]) - 0.25 * item.base[1]; const y2 = // @ts-ignore - 1.5 * (item.base[1] + data[3]) - 0.5 * (item.base[1] + data[5]); - if (Math.abs(y1 - y2) < error * 4) { + 0.75 * (item.base[1] + data[3]) - 0.25 * (item.base[1] + data[5]); + if (Math.abs(y1 - y2) < error * 2) { const newData = data.slice(); newData.splice( 0, 4, // @ts-ignore - (x1 + x2) / 2 - item.base[0], + (x1 + x2) - item.base[0], // @ts-ignore - (y1 + y2) / 2 - item.base[1], + (y1 + y2) - item.base[1], ); roundData(newData); const originalLength = cleanupOutData(data, params).length, From 4ef5d3a64cf34a3696272e872342068b431d4779 Mon Sep 17 00:00:00 2001 From: Kendell R Date: Mon, 1 Jan 2024 12:31:52 -0800 Subject: [PATCH 8/8] format suggested code --- plugins/convertPathData.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/convertPathData.js b/plugins/convertPathData.js index feab1724b..102cde4f1 100644 --- a/plugins/convertPathData.js +++ b/plugins/convertPathData.js @@ -722,9 +722,9 @@ function filters( 0, 4, // @ts-ignore - (x1 + x2) - item.base[0], + x1 + x2 - item.base[0], // @ts-ignore - (y1 + y2) - item.base[1], + y1 + y2 - item.base[1], ); roundData(newData); const originalLength = cleanupOutData(data, params).length,