Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add plugin simplifyPaths to reduce the number of points in paths and polylines while maintaining approximately the same overall shape #1698

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 4 additions & 2 deletions lib/builtin.js
Expand Up @@ -21,6 +21,7 @@ import * as minifyStyles from '../plugins/minifyStyles.js';
import * as moveElemsAttrsToGroup from '../plugins/moveElemsAttrsToGroup.js';
import * as moveGroupAttrsToElems from '../plugins/moveGroupAttrsToElems.js';
import * as prefixIds from '../plugins/prefixIds.js';
import * as simplifyPaths from '../plugins/simplifyPaths.js';
import * as removeAttributesBySelector from '../plugins/removeAttributesBySelector.js';
import * as removeAttrs from '../plugins/removeAttrs.js';
import * as removeComments from '../plugins/removeComments.js';
Expand Down Expand Up @@ -54,7 +55,6 @@ import * as sortAttrs from '../plugins/sortAttrs.js';
import * as sortDefsChildren from '../plugins/sortDefsChildren.js';

export const builtin = [
presetDefault,
addAttributesToSVGElement,
addClassesToSVGElement,
cleanupAttrs,
Expand All @@ -70,13 +70,14 @@ export const builtin = [
convertShapeToPath,
convertStyleToAttrs,
convertTransform,
mergeStyles,
inlineStyles,
mergePaths,
mergeStyles,
minifyStyles,
moveElemsAttrsToGroup,
moveGroupAttrsToElems,
prefixIds,
presetDefault,
removeAttributesBySelector,
removeAttrs,
removeComments,
Expand Down Expand Up @@ -106,6 +107,7 @@ export const builtin = [
removeXMLNS,
removeXMLProcInst,
reusePaths,
simplifyPaths,
sortAttrs,
sortDefsChildren,
];
106 changes: 106 additions & 0 deletions plugins/simplifyPaths.js
@@ -0,0 +1,106 @@
// vim: tabstop=2 softtabstop=2 shiftwidth=2 expandtab

export const name 'simplifyPaths';
export const type = 'visitor';
export const active = true;
export const description = 'Simplify (approximate) paths using Paper.js';

// simplifyThreshold: the allowed maximum error when fitting the curves through
// the segment points
// Set it to null to disable simplification
// http://paperjs.org/reference/path/#simplify
export const params = {
floatPrecision: 2,
path: {
simplifyThreshold: 2.5,
},
polyline: {
transform: true, // Whether to convert <polyline> into <path>
simplifyThreshold: 2.5,
},
};

const svgToString = (floatPrecision, path) => {
return path.exportSVG({ precision: floatPrecision }).attributes.d.value;
};

const simplify = (threshold, floatPrecision, path) => {
const original = svgToString(floatPrecision, path);

if (threshold === null) {
return original;
}

path.simplify(threshold);

const simplified = svgToString(floatPrecision, path);

return simplified.length < original.length ? simplified : original;
};

/**
* Simplify paths using Paper.js
* Convert <polyline> to <path> and simplify that too
*
* @see http://paperjs.org/reference/path/#simplify
*
* @param {Object} params plugin params
* @return {Boolean} if false, item will be filtered out
*
* @author Marcel Robitaille
*/
export const fn = (_root, params) => {
let paper;
try {
paper = require('paper');
} catch {
console.error(
'The `simplifyPaths` plugin requires `paper` to be installed.'
);
return false;
}

paper.setup();

const polylineParams = {
...exports.params.polyline,
...params.polyline,
};
const pathParams = {
...exports.params.path,
...params.path,
};
const floatPrecision = params.floatPrecision ?? exports.params.floatPrecision;

return {
element: {
enter: (node) => {
if (node.name === 'polyline' && polylineParams.transform) {
const points = node.attributes.points
.trim()
.split(' ')
.map((p) => p.split(',').map((x) => parseFloat(x)))
.map(([x, y]) => new paper.Point(x, y));
const path = new paper.Path(points);

// Convert the polyline to a path
node.name = 'path';
delete node.attributes['points'];

node.attributes.d = simplify(
polylineParams.simplifyThreshold,
floatPrecision,
path
);
} else if (node.name === 'path') {
const path = new paper.CompoundPath(node.attributes.d);
node.attributes.d = simplify(
pathParams.simplifyThreshold,
floatPrecision,
path
);
}
},
},
};
};
6 changes: 5 additions & 1 deletion test/plugins/_index.test.js
Expand Up @@ -33,7 +33,11 @@ describe('plugins tests', function () {
};
let lastResultData = original;
// test plugins idempotence
const exclude = ['addAttributesToSVGElement', 'convertTransform'];
const exclude = [
'addAttributesToSVGElement',
'convertTransform',
'simplifyPaths',
];
const multipass = exclude.includes(name) ? 1 : 2;
for (let i = 0; i < multipass; i += 1) {
const result = optimize(lastResultData, {
Expand Down
22 changes: 22 additions & 0 deletions test/plugins/simplifyPaths.01.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions test/plugins/simplifyPaths.02.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions test/plugins/simplifyPaths.03.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions test/plugins/simplifyPaths.04.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions test/plugins/simplifyPaths.05.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions test/plugins/simplifyPaths.06.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions test/plugins/simplifyPaths.07.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.