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

refactor(convertPaths): clean up plugin #1913

Merged
merged 1 commit into from
Dec 28, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 36 additions & 89 deletions plugins/convertPathData.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,15 @@ exports.name = 'convertPathData';
exports.description =
'optimizes path data: writes in shorter form, applies transformations';

/**
* @type {(data: number[]) => number[]}
*/
/** @type {(data: number[]) => number[]} */
let roundData;
/**
* @type {number | false}
*/
/** @type {number | false} */
let precision;
/**
* @type {number}
*/
/** @type {number} */
let error;
/**
* @type {number}
*/
/** @type {number} */
let arcThreshold;
/**
* @type {number}
*/
/** @type {number} */
let arcTolerance;

/**
Expand Down Expand Up @@ -393,16 +383,17 @@ function filters(
params,
{ isSafeToUseZ, maybeHasStrokeAndLinecap, hasMarkerMid },
) {
var stringify = data2Path.bind(null, params),
relSubpoint = [0, 0],
pathBase = [0, 0],
prev = {};
const stringify = data2Path.bind(null, params);
const relSubpoint = [0, 0];
const pathBase = [0, 0];
/** @type {any} */
let prev = {};
/** @type {Point | undefined} */
let qControlPoint;
let prevQControlPoint;

path = path.filter(function (item, index, path) {
const qPoint = qControlPoint;
qControlPoint = undefined;
const qControlPoint = prevQControlPoint;
prevQControlPoint = undefined;

let command = item.command;
let data = item.args;
Expand All @@ -415,9 +406,8 @@ function filters(
if (command === 's') {
sdata = [0, 0].concat(data);

// @ts-ignore
var pdata = prev.args,
n = pdata.length;
const pdata = prev.args;
const n = pdata.length;

// (-x, -y) of the prev tangent point relative to the current point
sdata[0] = pdata[n - 2] - pdata[n - 4];
Expand Down Expand Up @@ -464,24 +454,18 @@ function filters(
nextLonghand;

if (
// @ts-ignore
(prev.command == 'c' &&
// @ts-ignore
isConvex(prev.args) &&
// @ts-ignore
isArcPrev(prev.args, circle)) ||
// @ts-ignore
(prev.command == 'a' && prev.sdata && isArcPrev(prev.sdata, circle))
) {
// @ts-ignore
arcCurves.unshift(prev);
// @ts-ignore
arc.base = prev.base;
// @ts-ignore
arc.args[5] = arc.coords[0] - arc.base[0];
// @ts-ignore
arc.args[6] = arc.coords[1] - arc.base[1];
// @ts-ignore
var prevData = prev.command == 'a' ? prev.sdata : prev.args;
var prevAngle = findArcAngle(prevData, {
center: [
Expand All @@ -498,7 +482,7 @@ function filters(
// check if next curves are fitting the arc
for (
var j = index;
(next = path[++j]) && ~'cs'.indexOf(next.command);
(next = path[++j]) && 'cs'.includes(next.command);

) {
var nextData = next.args;
Expand Down Expand Up @@ -574,7 +558,6 @@ function filters(
relSubpoint[0] += prevArc.args[5] - prev.args[prev.args.length - 2];
// @ts-ignore
relSubpoint[1] += prevArc.args[6] - prev.args[prev.args.length - 1];
// @ts-ignore
prev.command = 'a';
// @ts-ignore
prev.args = prevArc.args;
Expand All @@ -588,11 +571,7 @@ function filters(
item.sdata = sdata.slice(); // preserve curve data for future checks
} else if (arcCurves.length - 1 - hasPrev > 0) {
// filter out consumed next items
path.splice.apply(
path,
// @ts-ignore
[index + 1, arcCurves.length - 1 - hasPrev].concat(output),
);
path.splice(index + 1, arcCurves.length - 1 - hasPrev, ...output);
}
if (!arc) return false;
command = 'a';
Expand Down Expand Up @@ -679,9 +658,7 @@ function filters(
data = data.slice(-2);
} else if (
command === 't' &&
// @ts-ignore
prev.command !== 'q' &&
// @ts-ignore
prev.command !== 't'
) {
command = 'l';
Expand Down Expand Up @@ -716,39 +693,29 @@ function filters(
params.collapseRepeated &&
hasMarkerMid === false &&
(command === 'm' || command === 'h' || command === 'v') &&
// @ts-ignore
prev.command &&
// @ts-ignore
command == prev.command.toLowerCase() &&
((command != 'h' && command != 'v') ||
// @ts-ignore
prev.args[0] >= 0 == data[0] >= 0)
) {
// @ts-ignore
prev.args[0] += data[0];
if (command != 'h' && command != 'v') {
// @ts-ignore
prev.args[1] += data[1];
}
// @ts-ignore
prev.coords = item.coords;
// @ts-ignore
path[index] = prev;
return false;
}

// convert curves into smooth shorthands
// @ts-ignore
if (params.curveSmoothShorthands && prev.command) {
// curveto
if (command === 'c') {
// c + c → c + s
if (
// @ts-ignore
prev.command === 'c' &&
// @ts-ignore
Math.abs(data[0] - -(prev.args[2] - prev.args[4])) < error &&
// @ts-ignore
Math.abs(data[1] - -(prev.args[3] - prev.args[5])) < error
) {
command = 's';
Expand All @@ -757,11 +724,8 @@ function filters(

// s + c → s + s
else if (
// @ts-ignore
prev.command === 's' &&
// @ts-ignore
Math.abs(data[0] - -(prev.args[0] - prev.args[2])) < error &&
// @ts-ignore
Math.abs(data[1] - -(prev.args[1] - prev.args[3])) < error
) {
command = 's';
Expand All @@ -770,9 +734,7 @@ function filters(

// [^cs] + c → [^cs] + s
else if (
// @ts-ignore
prev.command !== 'c' &&
// @ts-ignore
prev.command !== 's' &&
Math.abs(data[0]) < error &&
Math.abs(data[1]) < error
Expand All @@ -786,24 +748,22 @@ function filters(
else if (command === 'q') {
// q + q → q + t
if (
// @ts-ignore
prev.command === 'q' &&
// @ts-ignore
Math.abs(data[0] - (prev.args[2] - prev.args[0])) < error &&
// @ts-ignore
Math.abs(data[1] - (prev.args[3] - prev.args[1])) < error
) {
command = 't';
data = data.slice(2);
}

// t + q → t + t
else if (
// @ts-ignore
prev.command === 't'
) {
// @ts-ignore
const predictedControlPoint = reflectPoint(qPoint, item.base);
else if (prev.command === 't') {
const predictedControlPoint = reflectPoint(
// @ts-ignore
qControlPoint,
// @ts-ignore
item.base,
);
const realControlPoint = [
// @ts-ignore
data[0] + item.base[0],
Expand Down Expand Up @@ -837,14 +797,12 @@ function filters(
return i === 0;
})
) {
// @ts-ignore
path[index] = prev;
return false;
}

// a 25,25 -30 0,1 0,0
if (command === 'a' && data[5] === 0 && data[6] === 0) {
// @ts-ignore
path[index] = prev;
return false;
}
Expand Down Expand Up @@ -874,7 +832,6 @@ function filters(
// z resets coordinates
relSubpoint[0] = pathBase[0];
relSubpoint[1] = pathBase[1];
// @ts-ignore
if (prev.command === 'Z' || prev.command === 'z') return false;
}
if (
Expand All @@ -890,14 +847,14 @@ function filters(

if (command === 'q') {
// @ts-ignore
qControlPoint = [data[0] + item.base[0], data[1] + item.base[1]];
prevQControlPoint = [data[0] + item.base[0], data[1] + item.base[1]];
} else if (command === 't') {
if (qPoint) {
if (qControlPoint) {
// @ts-ignore
qControlPoint = reflectPoint(qPoint, item.base);
prevQControlPoint = reflectPoint(qControlPoint, item.base);
} else {
// @ts-ignore
qControlPoint = item.coords;
prevQControlPoint = item.coords;
}
}
prev = item;
Expand Down Expand Up @@ -971,8 +928,9 @@ function convertToMixed(path, params) {
prev.command.charCodeAt(0) > 96 &&
absoluteDataStr.length == relativeDataStr.length - 1 &&
(data[0] < 0 ||
// @ts-ignore
(/^0\./.test(data[0]) && prev.args[prev.args.length - 1] % 1))
(Math.floor(data[0]) === 0 &&
!Number.isInteger(data[0]) &&
prev.args[prev.args.length - 1] % 1))
))
) {
// @ts-ignore
Expand All @@ -981,7 +939,6 @@ function convertToMixed(path, params) {
}

prev = item;

return true;
});

Expand Down Expand Up @@ -1098,7 +1055,6 @@ function round(data) {
*
* @type {(data: number[]) => boolean}
*/

function isCurveStraightLine(data) {
// Get line equation a·x + b·y + c = 0 coefficients a, b (c = 0) by start and end points.
var i = data.length - 2,
Expand All @@ -1125,7 +1081,6 @@ function isCurveStraightLine(data) {
*/
function calculateSagitta(data) {
if (data[3] === 1) return undefined;

const [rx, ry] = data;
if (Math.abs(rx - ry) > error) return undefined;
const chord = Math.sqrt(data[5] ** 2 + data[6] ** 2);
Expand All @@ -1138,7 +1093,6 @@ function calculateSagitta(data) {
*
* @type {(item: PathDataItem, data: number[]) => PathDataItem}
*/

function makeLonghand(item, data) {
switch (item.command) {
case 's':
Expand All @@ -1160,20 +1114,19 @@ function makeLonghand(item, data) {
*
* @type {(point1: Point, point2: Point) => number}
*/

function getDistance(point1, point2) {
return Math.hypot(point1[0] - point2[0], point1[1] - point2[1]);
return Math.sqrt((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2);
}

/**
* Reflects point across another point
* Reflects point across another point.
*
* @param {Point} input
* @param {Point} controlPoint
* @param {Point} base
* @returns {Point}
*/
function reflectPoint(input, base) {
return [2 * base[0] - input[0], 2 * base[1] - input[1]];
function reflectPoint(controlPoint, base) {
return [2 * base[0] - controlPoint[0], 2 * base[1] - controlPoint[1]];
}

/**
Expand All @@ -1183,7 +1136,6 @@ function reflectPoint(input, base) {
*
* @type {(curve: number[], t: number) => Point}
*/

function getCubicBezierPoint(curve, t) {
var sqrT = t * t,
cubT = sqrT * t,
Expand All @@ -1201,7 +1153,6 @@ function getCubicBezierPoint(curve, t) {
*
* @type {(curve: number[]) => undefined | Circle}
*/

function findCircle(curve) {
var midPoint = getCubicBezierPoint(curve, 1 / 2),
m1 = [midPoint[0] / 2, midPoint[1] / 2],
Expand Down Expand Up @@ -1242,7 +1193,6 @@ function findCircle(curve) {
*
* @type {(curve: number[], circle: Circle) => boolean}
*/

function isArc(curve, circle) {
var tolerance = Math.min(
arcThreshold * error,
Expand All @@ -1264,7 +1214,6 @@ function isArc(curve, circle) {
*
* @type {(curve: number[], circle: Circle) => boolean}
*/

function isArcPrev(curve, circle) {
return isArc(curve, {
center: [circle.center[0] + curve[4], circle.center[1] + curve[5]],
Expand All @@ -1277,7 +1226,6 @@ function isArcPrev(curve, circle) {

* @type {(curve: number[], relCircle: Circle) => number}
*/

function findArcAngle(curve, relCircle) {
var x1 = -relCircle.center[0],
y1 = -relCircle.center[1],
Expand All @@ -1294,7 +1242,6 @@ function findArcAngle(curve, relCircle) {
*
* @type {(params: InternalParams, pathData: PathDataItem[]) => string}
*/

function data2Path(params, pathData) {
return pathData.reduce(function (pathString, item) {
var strData = '';
Expand Down