animate/webGl/my-threejs-test/node_modules/svgo/plugins/mergePaths.js

149 lines
4.1 KiB
JavaScript
Raw Normal View History

2024-06-24 09:24:00 +00:00
'use strict';
/**
* @typedef {import("../lib/types").PathDataItem} PathDataItem
* @typedef {import('../lib/types').XastChild} XastChild
* @typedef {import('../lib/types').XastElement} XastElement
*/
const { collectStylesheet, computeStyle } = require('../lib/style.js');
const { path2js, js2path, intersects } = require('./_path.js');
exports.name = 'mergePaths';
exports.description = 'merges multiple paths in one if possible';
/**
* Merge multiple Paths into one.
*
* @author Kir Belevich, Lev Solntsev
*
* @type {import('./plugins-types').Plugin<'mergePaths'>}
*/
exports.fn = (root, params) => {
const {
force = false,
floatPrecision,
noSpaceAfterFlags = false, // a20 60 45 0 1 30 20 → a20 60 45 0130 20
} = params;
const stylesheet = collectStylesheet(root);
return {
element: {
enter: (node) => {
if (node.children.length <= 1) {
return;
}
/** @type {XastChild[]} */
const elementsToRemove = [];
let prevChild = node.children[0];
let prevPathData = null;
/**
* @param {XastElement} child
* @param {PathDataItem[]} pathData
*/
const updatePreviousPath = (child, pathData) => {
js2path(child, pathData, {
floatPrecision,
noSpaceAfterFlags,
});
prevPathData = null;
};
for (let i = 1; i < node.children.length; i++) {
const child = node.children[i];
if (
prevChild.type !== 'element' ||
prevChild.name !== 'path' ||
prevChild.children.length !== 0 ||
prevChild.attributes.d == null
) {
if (prevPathData && prevChild.type === 'element') {
updatePreviousPath(prevChild, prevPathData);
}
prevChild = child;
continue;
}
if (
child.type !== 'element' ||
child.name !== 'path' ||
child.children.length !== 0 ||
child.attributes.d == null
) {
if (prevPathData) {
updatePreviousPath(prevChild, prevPathData);
}
prevChild = child;
continue;
}
const computedStyle = computeStyle(stylesheet, child);
if (
computedStyle['marker-start'] ||
computedStyle['marker-mid'] ||
computedStyle['marker-end']
) {
if (prevPathData) {
updatePreviousPath(prevChild, prevPathData);
}
prevChild = child;
continue;
}
const childAttrs = Object.keys(child.attributes);
if (childAttrs.length !== Object.keys(prevChild.attributes).length) {
if (prevPathData) {
updatePreviousPath(prevChild, prevPathData);
}
prevChild = child;
continue;
}
const areAttrsEqual = childAttrs.some((attr) => {
return (
attr !== 'd' &&
prevChild.type === 'element' &&
prevChild.attributes[attr] !== child.attributes[attr]
);
});
if (areAttrsEqual) {
if (prevPathData) {
updatePreviousPath(prevChild, prevPathData);
}
prevChild = child;
continue;
}
const hasPrevPath = prevPathData != null;
const currentPathData = path2js(child);
prevPathData = prevPathData ?? path2js(prevChild);
if (force || !intersects(prevPathData, currentPathData)) {
prevPathData.push(...currentPathData);
elementsToRemove.push(child);
continue;
}
if (hasPrevPath) {
updatePreviousPath(prevChild, prevPathData);
}
prevChild = child;
prevPathData = null;
}
if (prevPathData && prevChild.type === 'element') {
updatePreviousPath(prevChild, prevPathData);
}
node.children = node.children.filter(
(child) => !elementsToRemove.includes(child),
);
},
},
};
};