svg-path-simplify 0.0.4 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +193 -11
- package/dist/svg-path-simplify.esm.js +241 -130
- package/dist/svg-path-simplify.esm.min.js +1 -1
- package/dist/svg-path-simplify.js +4068 -3958
- package/dist/svg-path-simplify.min.js +1 -1
- package/dist/svg-path-simplify.node.js +240 -130
- package/dist/svg-path-simplify.node.min.js +1 -1
- package/index.html +6 -1
- package/package.json +2 -1
- package/src/detect_input.js +2 -0
- package/src/index.js +4 -4
- package/src/pathData_simplify_cubic_extrapolate.js +6 -3
- package/src/pathSimplify-main.js +120 -64
- package/src/svgii/pathData_convert.js +7 -3
- package/src/svgii/pathData_remove_collinear.js +10 -2
- package/src/svgii/pathData_remove_zerolength.js +16 -0
- package/src/svgii/pathData_reorder.js +9 -4
- package/src/svgii/pathData_split.js +4 -0
- package/src/svgii/pathData_toPolygon.js +98 -47
- package/src/svgii/poly_analyze.js +20 -16
- package/src/svgii/svg_cleanup.js +10 -1
- package/test.js +14 -0
- /package/src/svgii/{simplify_polygon.js → polygon_unite.js} +0 -0
|
@@ -1,40 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
function renderPoint(
|
|
4
|
-
svg,
|
|
5
|
-
coords,
|
|
6
|
-
fill = "red",
|
|
7
|
-
r = "1%",
|
|
8
|
-
opacity = "1",
|
|
9
|
-
title = '',
|
|
10
|
-
render = true,
|
|
11
|
-
id = "",
|
|
12
|
-
className = ""
|
|
13
|
-
) {
|
|
14
|
-
if (Array.isArray(coords)) {
|
|
15
|
-
coords = {
|
|
16
|
-
x: coords[0],
|
|
17
|
-
y: coords[1]
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
let marker = `<circle class="${className}" opacity="${opacity}" id="${id}" cx="${coords.x}" cy="${coords.y}" r="${r}" fill="${fill}">
|
|
21
|
-
<title>${title}</title></circle>`;
|
|
22
|
-
|
|
23
|
-
if (render) {
|
|
24
|
-
svg.insertAdjacentHTML("beforeend", marker);
|
|
25
|
-
} else {
|
|
26
|
-
return marker;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
3
|
function detectInputType(input) {
|
|
31
4
|
let type = 'string';
|
|
5
|
+
/*
|
|
32
6
|
if (input instanceof HTMLImageElement) return "img";
|
|
33
7
|
if (input instanceof SVGElement) return "svg";
|
|
34
8
|
if (input instanceof HTMLCanvasElement) return "canvas";
|
|
35
9
|
if (input instanceof File) return "file";
|
|
36
10
|
if (input instanceof ArrayBuffer) return "buffer";
|
|
37
11
|
if (input instanceof Blob) return "blob";
|
|
12
|
+
*/
|
|
38
13
|
if (Array.isArray(input)) return "array";
|
|
39
14
|
|
|
40
15
|
if (typeof input === "string") {
|
|
@@ -884,6 +859,7 @@ function addExtemesToCommand(p0, values, tMin=0, tMax=1) {
|
|
|
884
859
|
|
|
885
860
|
if(tArr.length){
|
|
886
861
|
let commandsSplit = splitCommandAtTValues(p0, values, tArr);
|
|
862
|
+
|
|
887
863
|
pathDataNew.push(...commandsSplit);
|
|
888
864
|
extremeCount += commandsSplit.length;
|
|
889
865
|
}else {
|
|
@@ -1537,6 +1513,7 @@ function getCombinedByDominant(com1, com2, maxDist = 0, tolerance = 1) {
|
|
|
1537
1513
|
let dP = cubicDerivative(com2.p0, com2.cp1, com2.cp2, com2.p, t0);
|
|
1538
1514
|
let r = sub(P, com1.p0);
|
|
1539
1515
|
|
|
1516
|
+
|
|
1540
1517
|
t0 -= dot(r, dP) / dot(dP, dP);
|
|
1541
1518
|
|
|
1542
1519
|
// construct merged cubic over [t0, 1]
|
|
@@ -1568,7 +1545,9 @@ function getCombinedByDominant(com1, com2, maxDist = 0, tolerance = 1) {
|
|
|
1568
1545
|
};
|
|
1569
1546
|
}
|
|
1570
1547
|
|
|
1571
|
-
let
|
|
1548
|
+
let tMid = (1 - t0)*0.5 ;
|
|
1549
|
+
|
|
1550
|
+
let ptM = pointAtT([result.p0, result.cp1, result.cp2, result.p], tMid, false, true);
|
|
1572
1551
|
let seg1_cp2 = ptM.cpts[2];
|
|
1573
1552
|
|
|
1574
1553
|
let ptI_1 = checkLineIntersection(ptM, seg1_cp2, result.p0, ptI, false);
|
|
@@ -2115,17 +2094,21 @@ function revertCubicQuadratic(p0 = {}, cp1 = {}, cp2 = {}, p = {}) {
|
|
|
2115
2094
|
let cp1_Q = null;
|
|
2116
2095
|
let type = 'C';
|
|
2117
2096
|
let values = [cp1.x, cp1.y, cp2.x, cp2.y, p.x, p.y];
|
|
2097
|
+
let comN = {type, values};
|
|
2118
2098
|
|
|
2119
2099
|
if (dist1 < threshold) {
|
|
2120
2100
|
cp1_Q = checkLineIntersection(p0, cp1, p, cp2, false);
|
|
2121
2101
|
if (cp1_Q) {
|
|
2122
2102
|
|
|
2123
|
-
type = 'Q';
|
|
2124
|
-
values = [cp1_Q.x, cp1_Q.y, p.x, p.y];
|
|
2103
|
+
comN.type = 'Q';
|
|
2104
|
+
comN.values = [cp1_Q.x, cp1_Q.y, p.x, p.y];
|
|
2105
|
+
comN.p0 = p0;
|
|
2106
|
+
comN.cp1 = cp1_Q;
|
|
2107
|
+
comN.p = p;
|
|
2125
2108
|
}
|
|
2126
2109
|
}
|
|
2127
2110
|
|
|
2128
|
-
return
|
|
2111
|
+
return comN
|
|
2129
2112
|
|
|
2130
2113
|
}
|
|
2131
2114
|
|
|
@@ -3502,8 +3485,11 @@ function pathDataRemoveColinear(pathData, tolerance = 1, flatBezierToLinetos = t
|
|
|
3502
3485
|
|
|
3503
3486
|
let area = getPolygonArea([p0, p, p1], true);
|
|
3504
3487
|
|
|
3488
|
+
getSquareDistance(p0, p);
|
|
3489
|
+
getSquareDistance(p, p1);
|
|
3505
3490
|
let distSquare = getSquareDistance(p0, p1);
|
|
3506
|
-
|
|
3491
|
+
|
|
3492
|
+
let distMax = distSquare / 200 * tolerance;
|
|
3507
3493
|
|
|
3508
3494
|
let isFlat = area < distMax;
|
|
3509
3495
|
let isFlatBez = false;
|
|
@@ -3533,7 +3519,10 @@ function pathDataRemoveColinear(pathData, tolerance = 1, flatBezierToLinetos = t
|
|
|
3533
3519
|
p0 = p;
|
|
3534
3520
|
|
|
3535
3521
|
// colinear – exclude arcs (as always =) as semicircles won't have an area
|
|
3536
|
-
|
|
3522
|
+
|
|
3523
|
+
if ( isFlat && c < l - 1 && (type === 'L' || (flatBezierToLinetos && isFlatBez)) ) {
|
|
3524
|
+
|
|
3525
|
+
|
|
3537
3526
|
|
|
3538
3527
|
continue;
|
|
3539
3528
|
}
|
|
@@ -3556,6 +3545,21 @@ function pathDataRemoveColinear(pathData, tolerance = 1, flatBezierToLinetos = t
|
|
|
3556
3545
|
|
|
3557
3546
|
}
|
|
3558
3547
|
|
|
3548
|
+
// remove zero-length segments introduced by rounding
|
|
3549
|
+
function removeZeroLengthLinetos_post(pathData) {
|
|
3550
|
+
let pathDataOpt = [];
|
|
3551
|
+
pathData.forEach((com, i) => {
|
|
3552
|
+
let { type, values } = com;
|
|
3553
|
+
if (type === 'l' || type === 'v' || type === 'h') {
|
|
3554
|
+
let hasLength = type === 'l' ? (values.join('') !== '00') : values[0] !== 0;
|
|
3555
|
+
if (hasLength) pathDataOpt.push(com);
|
|
3556
|
+
} else {
|
|
3557
|
+
pathDataOpt.push(com);
|
|
3558
|
+
}
|
|
3559
|
+
});
|
|
3560
|
+
return pathDataOpt
|
|
3561
|
+
}
|
|
3562
|
+
|
|
3559
3563
|
function removeZeroLengthLinetos(pathData) {
|
|
3560
3564
|
|
|
3561
3565
|
let M = { x: pathData[0].values[0], y: pathData[0].values[1] };
|
|
@@ -3609,12 +3613,98 @@ function pathDataToTopLeft(pathData) {
|
|
|
3609
3613
|
}
|
|
3610
3614
|
|
|
3611
3615
|
// reorder to top left most
|
|
3612
|
-
|
|
3616
|
+
|
|
3617
|
+
indices = indices.sort((a, b) => +a.y.toFixed(3) - +b.y.toFixed(3) );
|
|
3613
3618
|
newIndex = indices[0].index;
|
|
3614
3619
|
|
|
3615
3620
|
return newIndex ? shiftSvgStartingPoint(pathData, newIndex) : pathData;
|
|
3616
3621
|
}
|
|
3617
3622
|
|
|
3623
|
+
function optimizeClosePath(pathData, removeFinalLineto = true, reorder = true) {
|
|
3624
|
+
|
|
3625
|
+
let pathDataNew = [];
|
|
3626
|
+
let len = pathData.length;
|
|
3627
|
+
let M = { x: +pathData[0].values[0].toFixed(8), y: +pathData[0].values[1].toFixed(8) };
|
|
3628
|
+
let isClosed = pathData[len - 1].type.toLowerCase() === 'z';
|
|
3629
|
+
|
|
3630
|
+
let linetos = pathData.filter(com => com.type === 'L');
|
|
3631
|
+
|
|
3632
|
+
// check if order is ideal
|
|
3633
|
+
let penultimateCom = pathData[len - 2];
|
|
3634
|
+
let penultimateType = penultimateCom.type;
|
|
3635
|
+
let penultimateComCoords = penultimateCom.values.slice(-2).map(val => +val.toFixed(8));
|
|
3636
|
+
|
|
3637
|
+
// last L command ends at M
|
|
3638
|
+
let isClosingCommand = penultimateComCoords[0] === M.x && penultimateComCoords[1] === M.y;
|
|
3639
|
+
|
|
3640
|
+
// if last segment is not closing or a lineto
|
|
3641
|
+
let skipReorder = pathData[1].type !== 'L' && (!isClosingCommand || penultimateType === 'L');
|
|
3642
|
+
skipReorder = false;
|
|
3643
|
+
|
|
3644
|
+
// we can't change starting point for non closed paths
|
|
3645
|
+
if (!isClosed) {
|
|
3646
|
+
return pathData
|
|
3647
|
+
}
|
|
3648
|
+
|
|
3649
|
+
let newIndex = 0;
|
|
3650
|
+
|
|
3651
|
+
if (!skipReorder) {
|
|
3652
|
+
|
|
3653
|
+
let indices = [];
|
|
3654
|
+
for (let i = 0, len = pathData.length; i < len; i++) {
|
|
3655
|
+
let com = pathData[i];
|
|
3656
|
+
let { type, values } = com;
|
|
3657
|
+
if (values.length) {
|
|
3658
|
+
let valsL = values.slice(-2);
|
|
3659
|
+
let prevL = pathData[i - 1] && pathData[i - 1].type === 'L';
|
|
3660
|
+
let nextL = pathData[i + 1] && pathData[i + 1].type === 'L';
|
|
3661
|
+
let prevCom = pathData[i - 1] ? pathData[i - 1].type.toUpperCase() : null;
|
|
3662
|
+
let nextCom = pathData[i + 1] ? pathData[i + 1].type.toUpperCase() : null;
|
|
3663
|
+
let p = { type: type, x: valsL[0], y: valsL[1], dist: 0, index: 0, prevL, nextL, prevCom, nextCom };
|
|
3664
|
+
p.index = i;
|
|
3665
|
+
indices.push(p);
|
|
3666
|
+
}
|
|
3667
|
+
}
|
|
3668
|
+
|
|
3669
|
+
// find top most lineto
|
|
3670
|
+
|
|
3671
|
+
if (linetos.length) {
|
|
3672
|
+
let curveAfterLine = indices.filter(com => (com.type !== 'L' && com.type !== 'M') && com.prevCom &&
|
|
3673
|
+
com.prevCom === 'L' || com.prevCom === 'M' && penultimateType === 'L').sort((a, b) => a.y - b.y || a.x - b.x)[0];
|
|
3674
|
+
|
|
3675
|
+
newIndex = curveAfterLine ? curveAfterLine.index - 1 : 0;
|
|
3676
|
+
|
|
3677
|
+
}
|
|
3678
|
+
// use top most command
|
|
3679
|
+
else {
|
|
3680
|
+
indices = indices.sort((a, b) => +a.y.toFixed(1) - +b.y.toFixed(1) || a.x - b.x);
|
|
3681
|
+
newIndex = indices[0].index;
|
|
3682
|
+
}
|
|
3683
|
+
|
|
3684
|
+
// reorder
|
|
3685
|
+
pathData = newIndex ? shiftSvgStartingPoint(pathData, newIndex) : pathData;
|
|
3686
|
+
}
|
|
3687
|
+
|
|
3688
|
+
M = { x: +pathData[0].values[0].toFixed(8), y: +pathData[0].values[1].toFixed(7) };
|
|
3689
|
+
|
|
3690
|
+
len = pathData.length;
|
|
3691
|
+
|
|
3692
|
+
// remove last lineto
|
|
3693
|
+
penultimateCom = pathData[len - 2];
|
|
3694
|
+
penultimateType = penultimateCom.type;
|
|
3695
|
+
penultimateComCoords = penultimateCom.values.slice(-2).map(val=>+val.toFixed(8));
|
|
3696
|
+
|
|
3697
|
+
isClosingCommand = penultimateType === 'L' && penultimateComCoords[0] === M.x && penultimateComCoords[1] === M.y;
|
|
3698
|
+
|
|
3699
|
+
if (removeFinalLineto && isClosingCommand) {
|
|
3700
|
+
pathData.splice(len - 2, 1);
|
|
3701
|
+
}
|
|
3702
|
+
|
|
3703
|
+
pathDataNew.push(...pathData);
|
|
3704
|
+
|
|
3705
|
+
return pathDataNew
|
|
3706
|
+
}
|
|
3707
|
+
|
|
3618
3708
|
/**
|
|
3619
3709
|
* shift starting point
|
|
3620
3710
|
*/
|
|
@@ -3828,6 +3918,13 @@ function reversePathData(pathData, {
|
|
|
3828
3918
|
return pathDataNew;
|
|
3829
3919
|
}
|
|
3830
3920
|
|
|
3921
|
+
function removeEmptySVGEls(svg) {
|
|
3922
|
+
let els = svg.querySelectorAll('g, defs');
|
|
3923
|
+
els.forEach(el => {
|
|
3924
|
+
if (!el.children.length) el.remove();
|
|
3925
|
+
});
|
|
3926
|
+
}
|
|
3927
|
+
|
|
3831
3928
|
function cleanUpSVG(svgMarkup, {
|
|
3832
3929
|
returnDom=false,
|
|
3833
3930
|
removeHidden=true,
|
|
@@ -3915,10 +4012,13 @@ function stringifySVG(svg){
|
|
|
3915
4012
|
}
|
|
3916
4013
|
|
|
3917
4014
|
function svgPathSimplify(input = '', {
|
|
4015
|
+
|
|
4016
|
+
// return svg markup or object
|
|
4017
|
+
getObject = false,
|
|
4018
|
+
|
|
3918
4019
|
toAbsolute = true,
|
|
3919
4020
|
toRelative = true,
|
|
3920
4021
|
toShorthands = true,
|
|
3921
|
-
decimals = 3,
|
|
3922
4022
|
|
|
3923
4023
|
// not necessary unless you need cubics only
|
|
3924
4024
|
quadraticToCubic = true,
|
|
@@ -3927,30 +4027,31 @@ function svgPathSimplify(input = '', {
|
|
|
3927
4027
|
arcToCubic = false,
|
|
3928
4028
|
cubicToArc = false,
|
|
3929
4029
|
|
|
3930
|
-
|
|
3931
|
-
arcAccuracy = 4,
|
|
3932
|
-
keepExtremes = true,
|
|
3933
|
-
keepCorners = true,
|
|
3934
|
-
keepInflections = true,
|
|
3935
|
-
extrapolateDominant = false,
|
|
3936
|
-
addExtremes = false,
|
|
4030
|
+
simplifyBezier = true,
|
|
3937
4031
|
optimizeOrder = true,
|
|
3938
4032
|
removeColinear = true,
|
|
3939
|
-
simplifyBezier = true,
|
|
3940
|
-
autoAccuracy = true,
|
|
3941
4033
|
flatBezierToLinetos = true,
|
|
3942
4034
|
revertToQuadratics = true,
|
|
4035
|
+
|
|
4036
|
+
keepExtremes = true,
|
|
4037
|
+
keepCorners = true,
|
|
4038
|
+
extrapolateDominant = true,
|
|
4039
|
+
keepInflections = false,
|
|
4040
|
+
addExtremes = false,
|
|
4041
|
+
|
|
4042
|
+
// svg path optimizations
|
|
4043
|
+
decimals = 3,
|
|
4044
|
+
autoAccuracy = true,
|
|
4045
|
+
|
|
3943
4046
|
minifyD = 0,
|
|
3944
4047
|
tolerance = 1,
|
|
3945
4048
|
reverse = false,
|
|
3946
4049
|
|
|
3947
4050
|
// svg cleanup options
|
|
4051
|
+
mergePaths = false,
|
|
3948
4052
|
removeHidden = true,
|
|
3949
4053
|
removeUnused = true,
|
|
3950
4054
|
|
|
3951
|
-
// return svg markup or object
|
|
3952
|
-
getObject = false
|
|
3953
|
-
|
|
3954
4055
|
} = {}) {
|
|
3955
4056
|
|
|
3956
4057
|
// clamp tolerance
|
|
@@ -4002,6 +4103,17 @@ function svgPathSimplify(input = '', {
|
|
|
4002
4103
|
/**
|
|
4003
4104
|
* process all paths
|
|
4004
4105
|
*/
|
|
4106
|
+
|
|
4107
|
+
// SVG optimization options
|
|
4108
|
+
let pathOptions = {
|
|
4109
|
+
toRelative,
|
|
4110
|
+
toShorthands,
|
|
4111
|
+
decimals,
|
|
4112
|
+
};
|
|
4113
|
+
|
|
4114
|
+
// combinded path data for SVGs with mergePaths enabled
|
|
4115
|
+
let pathData_merged = [];
|
|
4116
|
+
|
|
4005
4117
|
paths.forEach(path => {
|
|
4006
4118
|
let { d, el } = path;
|
|
4007
4119
|
|
|
@@ -4048,7 +4160,7 @@ function svgPathSimplify(input = '', {
|
|
|
4048
4160
|
// simplify beziers
|
|
4049
4161
|
let { pathData, bb, dimA } = pathDataPlus;
|
|
4050
4162
|
|
|
4051
|
-
pathData = simplifyBezier ?
|
|
4163
|
+
pathData = simplifyBezier ? simplifyPathDataCubic(pathData, { simplifyBezier, keepInflections, keepExtremes, keepCorners, extrapolateDominant, revertToQuadratics, tolerance, reverse }) : pathData;
|
|
4052
4164
|
|
|
4053
4165
|
// cubic to arcs
|
|
4054
4166
|
if (cubicToArc) {
|
|
@@ -4077,11 +4189,27 @@ function svgPathSimplify(input = '', {
|
|
|
4077
4189
|
if (type === 'C') {
|
|
4078
4190
|
|
|
4079
4191
|
let comQ = revertCubicQuadratic(p0, cp1, cp2, p);
|
|
4080
|
-
if (comQ.type === 'Q')
|
|
4192
|
+
if (comQ.type === 'Q') {
|
|
4193
|
+
/*
|
|
4194
|
+
comQ.p0 = com.p0
|
|
4195
|
+
comQ.cp1 = {x:comQ.values[0], y:comQ.values[1]}
|
|
4196
|
+
comQ.p = com.p
|
|
4197
|
+
*/
|
|
4198
|
+
comQ.extreme = com.extreme;
|
|
4199
|
+
comQ.corner = com.corner;
|
|
4200
|
+
comQ.dimA = com.dimA;
|
|
4201
|
+
|
|
4202
|
+
pathData[c] = comQ;
|
|
4203
|
+
}
|
|
4081
4204
|
}
|
|
4082
4205
|
});
|
|
4083
4206
|
}
|
|
4084
4207
|
|
|
4208
|
+
// optimize close path
|
|
4209
|
+
if (optimizeOrder) pathData = optimizeClosePath(pathData);
|
|
4210
|
+
|
|
4211
|
+
// poly
|
|
4212
|
+
|
|
4085
4213
|
// update
|
|
4086
4214
|
pathDataArrN.push(pathData);
|
|
4087
4215
|
}
|
|
@@ -4089,64 +4217,76 @@ function svgPathSimplify(input = '', {
|
|
|
4089
4217
|
// flatten compound paths
|
|
4090
4218
|
pathData = pathDataArrN.flat();
|
|
4091
4219
|
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
if (autoAccuracy) {
|
|
4096
|
-
decimals = detectAccuracy(pathData);
|
|
4220
|
+
// collect for merged svg paths
|
|
4221
|
+
if (el && mergePaths) {
|
|
4222
|
+
pathData_merged.push(...pathData);
|
|
4097
4223
|
}
|
|
4224
|
+
// single output
|
|
4225
|
+
else {
|
|
4098
4226
|
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4227
|
+
/**
|
|
4228
|
+
* detect accuracy
|
|
4229
|
+
*/
|
|
4230
|
+
if (autoAccuracy) {
|
|
4231
|
+
decimals = detectAccuracy(pathData);
|
|
4232
|
+
}
|
|
4105
4233
|
|
|
4106
|
-
|
|
4107
|
-
|
|
4234
|
+
// optimize path data
|
|
4235
|
+
pathData = convertPathData(pathData, pathOptions);
|
|
4108
4236
|
|
|
4109
|
-
|
|
4110
|
-
|
|
4237
|
+
// remove zero-length segments introduced by rounding
|
|
4238
|
+
pathData = removeZeroLengthLinetos_post(pathData);
|
|
4111
4239
|
|
|
4112
|
-
|
|
4113
|
-
let
|
|
4114
|
-
if (type === 'l' || type === 'v' || type === 'h') {
|
|
4115
|
-
let hasLength = type === 'l' ? (values.join('') !== '00') : values[0] !== 0;
|
|
4116
|
-
if (hasLength) pathDataOpt.push(com);
|
|
4117
|
-
} else {
|
|
4118
|
-
pathDataOpt.push(com);
|
|
4119
|
-
}
|
|
4120
|
-
});
|
|
4240
|
+
// compare command count
|
|
4241
|
+
let comCountS = pathData.length;
|
|
4121
4242
|
|
|
4122
|
-
|
|
4243
|
+
let dOpt = pathDataToD(pathData, minifyD);
|
|
4244
|
+
svgSizeOpt = new Blob([dOpt]).size;
|
|
4245
|
+
compression = +(100 / svgSize * (svgSizeOpt)).toFixed(2);
|
|
4123
4246
|
|
|
4124
|
-
|
|
4125
|
-
|
|
4247
|
+
path.d = dOpt;
|
|
4248
|
+
path.report = {
|
|
4249
|
+
original: comCount,
|
|
4250
|
+
new: comCountS,
|
|
4251
|
+
saved: comCount - comCountS,
|
|
4252
|
+
compression,
|
|
4253
|
+
decimals,
|
|
4126
4254
|
|
|
4127
|
-
|
|
4128
|
-
svgSizeOpt = new Blob([dOpt]).size;
|
|
4255
|
+
};
|
|
4129
4256
|
|
|
4130
|
-
|
|
4257
|
+
// apply new path for svgs
|
|
4258
|
+
if (el) el.setAttribute('d', dOpt);
|
|
4259
|
+
}
|
|
4260
|
+
});
|
|
4131
4261
|
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
saved: comCount - comCountS,
|
|
4137
|
-
compression,
|
|
4138
|
-
decimals,
|
|
4262
|
+
/**
|
|
4263
|
+
* stringify new SVG
|
|
4264
|
+
*/
|
|
4265
|
+
if (mode) {
|
|
4139
4266
|
|
|
4140
|
-
|
|
4267
|
+
if (pathData_merged.length) {
|
|
4268
|
+
// optimize path data
|
|
4269
|
+
let pathData = convertPathData(pathData_merged, pathOptions);
|
|
4141
4270
|
|
|
4142
|
-
|
|
4143
|
-
|
|
4271
|
+
// remove zero-length segments introduced by rounding
|
|
4272
|
+
pathData = removeZeroLengthLinetos_post(pathData);
|
|
4144
4273
|
|
|
4145
|
-
|
|
4274
|
+
let dOpt = pathDataToD(pathData, minifyD);
|
|
4146
4275
|
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4276
|
+
// apply new path for svgs
|
|
4277
|
+
paths[0].el.setAttribute('d', dOpt);
|
|
4278
|
+
|
|
4279
|
+
// remove other paths
|
|
4280
|
+
for (let i = 1; i < paths.length; i++) {
|
|
4281
|
+
let pathEl = paths[i].el;
|
|
4282
|
+
if (pathEl) pathEl.remove();
|
|
4283
|
+
}
|
|
4284
|
+
|
|
4285
|
+
// remove empty groups e.g groups
|
|
4286
|
+
removeEmptySVGEls(svg);
|
|
4287
|
+
}
|
|
4288
|
+
|
|
4289
|
+
svg = stringifySVG(svg);
|
|
4150
4290
|
svgSizeOpt = new Blob([svg]).size;
|
|
4151
4291
|
|
|
4152
4292
|
compression = +(100 / svgSize * (svgSizeOpt)).toFixed(2);
|
|
@@ -4168,7 +4308,7 @@ function svgPathSimplify(input = '', {
|
|
|
4168
4308
|
|
|
4169
4309
|
}
|
|
4170
4310
|
|
|
4171
|
-
function
|
|
4311
|
+
function simplifyPathDataCubic(pathData, {
|
|
4172
4312
|
keepExtremes = true,
|
|
4173
4313
|
keepInflections = true,
|
|
4174
4314
|
keepCorners = true,
|
|
@@ -4212,6 +4352,8 @@ function simplifyPathData(pathData, {
|
|
|
4212
4352
|
if (combined.length === 1) {
|
|
4213
4353
|
com = combined[0];
|
|
4214
4354
|
let offset = 1;
|
|
4355
|
+
|
|
4356
|
+
// add cumulative error to prevent distortions
|
|
4215
4357
|
error += com.error;
|
|
4216
4358
|
|
|
4217
4359
|
// find next candidates
|
|
@@ -4229,6 +4371,9 @@ function simplifyPathData(pathData, {
|
|
|
4229
4371
|
|
|
4230
4372
|
let combined = combineCubicPairs(com, comN, extrapolateDominant, tolerance);
|
|
4231
4373
|
if (combined.length === 1) {
|
|
4374
|
+
// add cumulative error to prevent distortions
|
|
4375
|
+
|
|
4376
|
+
error += combined[0].error * 0.5;
|
|
4232
4377
|
offset++;
|
|
4233
4378
|
}
|
|
4234
4379
|
com = combined[0];
|
|
@@ -4260,39 +4405,6 @@ function simplifyPathData(pathData, {
|
|
|
4260
4405
|
return pathDataN
|
|
4261
4406
|
}
|
|
4262
4407
|
|
|
4263
|
-
/**
|
|
4264
|
-
* get viewBox
|
|
4265
|
-
* either from explicit attribute or
|
|
4266
|
-
* width and height attributes
|
|
4267
|
-
*/
|
|
4268
|
-
|
|
4269
|
-
function getViewBox(svg = null, round = false) {
|
|
4270
|
-
|
|
4271
|
-
// browser default
|
|
4272
|
-
if (!svg) return { x: 0, y: 0, width: 300, height: 150 }
|
|
4273
|
-
|
|
4274
|
-
let style = window.getComputedStyle(svg);
|
|
4275
|
-
|
|
4276
|
-
// the baseVal API method also converts physical units to pixels/user-units
|
|
4277
|
-
let w = svg.hasAttribute('width') ? svg.width.baseVal.value : parseFloat(style.width) || 300;
|
|
4278
|
-
let h = svg.hasAttribute('height') ? svg.height.baseVal.value : parseFloat(style.height) || 150;
|
|
4279
|
-
|
|
4280
|
-
let viewBox = svg.getAttribute('viewBox') ? svg.viewBox.baseVal : { x: 0, y: 0, width: w, height: h };
|
|
4281
|
-
|
|
4282
|
-
// remove SVG constructor
|
|
4283
|
-
let { x, y, width, height } = viewBox;
|
|
4284
|
-
viewBox = { x, y, width, height };
|
|
4285
|
-
|
|
4286
|
-
// round to integers
|
|
4287
|
-
if (round) {
|
|
4288
|
-
for (let prop in viewBox) {
|
|
4289
|
-
viewBox[prop] = Math.ceil(viewBox[prop]);
|
|
4290
|
-
}
|
|
4291
|
-
}
|
|
4292
|
-
|
|
4293
|
-
return viewBox
|
|
4294
|
-
}
|
|
4295
|
-
|
|
4296
4408
|
const {
|
|
4297
4409
|
abs, acos, asin, atan, atan2, ceil, cos, exp, floor,
|
|
4298
4410
|
log, hypot, max, min, pow, random, round, sin, sqrt, tan, PI
|
|
@@ -4303,8 +4415,7 @@ const {
|
|
|
4303
4415
|
// IIFE
|
|
4304
4416
|
if (typeof window !== 'undefined') {
|
|
4305
4417
|
window.svgPathSimplify = svgPathSimplify;
|
|
4306
|
-
|
|
4307
|
-
window.renderPoint = renderPoint;
|
|
4418
|
+
|
|
4308
4419
|
}
|
|
4309
4420
|
|
|
4310
4421
|
exports.PI = PI;
|
|
@@ -4317,7 +4428,6 @@ exports.ceil = ceil;
|
|
|
4317
4428
|
exports.cos = cos;
|
|
4318
4429
|
exports.exp = exp;
|
|
4319
4430
|
exports.floor = floor;
|
|
4320
|
-
exports.getViewBox = getViewBox;
|
|
4321
4431
|
exports.hypot = hypot;
|
|
4322
4432
|
exports.log = log;
|
|
4323
4433
|
exports.max = max;
|