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