Lint collinear coordinates inside straight lines (#4589)

This commit is contained in:
Álvaro Mondéjar 2021-01-03 18:08:06 +01:00 committed by GitHub
parent 5b0d1d3e49
commit 45130ca86a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 153 additions and 0 deletions

File diff suppressed because one or more lines are too long

View file

@ -38,6 +38,14 @@ function removeLeadingZeros(number) {
return number.toString().replace(/^(-?)(0)(\.?.+)/, '$1$3');
}
/**
* Given three points, returns if the middle one (x2, y2) is collinear
* to the line formed by the two limit points.
**/
function collinear(x1, y1, x2, y2, x3, y3) {
return (x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) === 0;
}
if (updateIgnoreFile) {
process.on('exit', () => {
// ensure object output order is consistent due to async svglint processing
@ -300,6 +308,108 @@ module.exports = {
}
}
},
function(reporter, $, ast) {
reporter.name = "collinear-segments";
const iconPath = $.find("path").attr("d");
if (!updateIgnoreFile && isIgnored(reporter.name, iconPath)) {
return;
}
/**
* Extracts collinear coordinates from SVG path straight lines
* (does not extracts collinear coordinates from curves).
**/
const getCollinearSegments = (path) => {
const { segments } = svgPath(path).unarc().unshort(),
collinearSegments = [],
straightLineCommands = 'HhVvLlMm',
zCommands = 'Zz';
let currLine = [],
currAbsCoord = [undefined, undefined],
_inStraightLine = false,
_nextInStraightLine = false;
for (let s = 0; s < segments.length; s++) {
let seg = segments[s],
cmd = seg[0],
nextCmd = s + 1 < segments.length ? segments[s + 1][0] : null;
if ('LM'.includes(cmd)) {
currAbsCoord[0] = seg[1];
currAbsCoord[1] = seg[2];
} else if ('lm'.includes(cmd)) {
currAbsCoord[0] = (!currAbsCoord[0] ? 0 : currAbsCoord[0]) + seg[1];
currAbsCoord[1] = (!currAbsCoord[1] ? 0 : currAbsCoord[1]) + seg[2];
} else if (cmd === 'H') {
currAbsCoord[0] = seg[1];
} else if (cmd === 'h') {
currAbsCoord[0] = (!currAbsCoord[0] ? 0 : currAbsCoord[0]) + seg[1];
} else if (cmd === 'V') {
currAbsCoord[1] = seg[1];
} else if (cmd === 'v') {
currAbsCoord[1] = (!currAbsCoord[1] ? 0 : currAbsCoord[1]) + seg[1];
} else if (cmd === 'C') {
currAbsCoord[0] = seg[5];
currAbsCoord[1] = seg[6];
} else if (cmd === 'c') {
currAbsCoord[0] = (!currAbsCoord[0] ? 0 : currAbsCoord[0]) + seg[5];
currAbsCoord[1] = (!currAbsCoord[1] ? 0 : currAbsCoord[1]) + seg[6];
} else if (cmd === 'Q') {
currAbsCoord[0] = seg[3];
currAbsCoord[1] = seg[4];
} else if (cmd === 'q') {
currAbsCoord[0] = (!currAbsCoord[0] ? 0 : currAbsCoord[0]) + seg[3];
currAbsCoord[1] = (!currAbsCoord[1] ? 0 : currAbsCoord[1]) + seg[4];
} else if (zCommands.includes(cmd)) {
// Overlapping in Z should be handled in another rule
currAbsCoord = [undefined, undefined];
} else {
throw new Error(`"${cmd}" command not handled`)
}
_nextInStraightLine = straightLineCommands.includes(nextCmd);
let _exitingStraightLine = (_inStraightLine && !_nextInStraightLine);
_inStraightLine = straightLineCommands.includes(cmd);
if (_inStraightLine) {
currLine.push([currAbsCoord[0], currAbsCoord[1]]);
} else {
if (_exitingStraightLine) {
if (!zCommands.includes(cmd)) {
currLine.push([currAbsCoord[0], currAbsCoord[1]]);
}
// Get collinear coordinates
for (let p = 0; p < currLine.length; p++) {
if (p === 0 || p === currLine.length - 1) {
continue;
}
let _collinearCoord = collinear(currLine[p - 1][0],
currLine[p - 1][1],
currLine[p][0],
currLine[p][1],
currLine[p + 1][0],
currLine[p + 1][1])
if (_collinearCoord) {
collinearSegments.push(segments[s - currLine.length + p + 1]);
}
}
}
currLine = [];
}
}
return collinearSegments;
}
getCollinearSegments(iconPath).forEach((segment) => {
let segmentString = `${segment[0]}${segment[1]}`;
if ('LlMm'.includes(segment[0])) {
segmentString += ` ${segment[2]}`
}
reporter.error(`Collinear segment "${segmentString}" in path (should be removed)`);
});
},
function(reporter, $, ast) {
reporter.name = "extraneous";