svg-path-simplify 0.0.2 → 0.0.4

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.
@@ -1,3 +1,4 @@
1
+ import { detectInputType } from './detect_input';
1
2
  import { combineCubicPairs } from './pathData_simplify_cubic';
2
3
  import { getPathDataVertices, pointAtT } from './svgii/geometry';
3
4
  import { getPolyBBox } from './svgii/geometry_bbox';
@@ -14,9 +15,10 @@ import { pathDataToPolyPlus } from './svgii/pathData_toPolygon';
14
15
  import { analyzePoly } from './svgii/poly_analyze';
15
16
  import { getCurvePathData } from './svgii/poly_to_pathdata';
16
17
  import { detectAccuracy } from './svgii/rounding';
18
+ import { cleanUpSVG } from './svgii/svg_cleanup';
17
19
  import { renderPoint } from './svgii/visualize';
18
20
 
19
- export function svgPathSimplify(d = '', {
21
+ export function svgPathSimplify(input = '', {
20
22
  toAbsolute = true,
21
23
  toRelative = true,
22
24
  toShorthands = true,
@@ -45,147 +47,251 @@ export function svgPathSimplify(d = '', {
45
47
  revertToQuadratics = true,
46
48
  minifyD = 0,
47
49
  tolerance = 1,
48
- reverse = false
50
+ reverse = false,
51
+
52
+ // svg cleanup options
53
+ removeHidden = true,
54
+ removeUnused = true,
55
+
56
+ // return svg markup or object
57
+ getObject = false
58
+
49
59
  } = {}) {
50
60
 
61
+ // clamp tolerance
62
+ tolerance = Math.max(0.1, tolerance);
63
+
64
+ let inputType = detectInputType(input);
51
65
 
52
- let pathDataO = parsePathDataNormalized(d, { quadraticToCubic, toAbsolute, arcToCubic });
66
+ let svg = '';
67
+ let svgSize = 0;
68
+ let svgSizeOpt = 0;
69
+ let compression = 0;
70
+ let report = {};
71
+ let d = '';
72
+ let mode = inputType === 'svgMarkup' ? 1 : 0;
53
73
 
54
- // create clone for fallback
55
- let pathData = JSON.parse(JSON.stringify(pathDataO));
74
+ let paths = []
56
75
 
57
- // count commands for evaluation
58
- let comCount = pathDataO.length
59
76
 
60
77
  /**
61
- * get sub paths
78
+ * normalize input
79
+ * switch mode
62
80
  */
63
- let subPathArr = splitSubpaths(pathData);
64
81
 
65
- // cleaned up pathData
66
- let pathDataArrN = [];
82
+ // original size
83
+ svgSize = new Blob([input]).size;
67
84
 
68
- for (let i = 0, l = subPathArr.length; i < l; i++) {
85
+ // single path
86
+ if (!mode) {
87
+ if (inputType === 'pathDataString') {
88
+ d = input
89
+ } else if (inputType === 'polyString') {
90
+ d = 'M' + input
91
+ }
92
+ paths.push({ d, el: null })
93
+ }
94
+ // process svg
95
+ else {
96
+ //sanitize
97
+ let returnDom = true
98
+ svg = cleanUpSVG(input, { returnDom, removeHidden, removeUnused }
99
+ );
100
+
101
+ // collect paths
102
+ let pathEls = svg.querySelectorAll('path')
103
+ pathEls.forEach(path => {
104
+ paths.push({ d: path.getAttribute('d'), el: path })
105
+ })
106
+ }
69
107
 
70
- //let { pathData, bb } = subPathArr[i];
71
- let pathDataSub = subPathArr[i];
108
+ //console.log(paths);
109
+ //console.log('inputType', inputType, 'mode', mode);
72
110
 
73
- // try simplification in reversed order
74
- if (reverse) pathDataSub = reversePathData(pathDataSub);
111
+ /**
112
+ * process all paths
113
+ */
114
+ paths.forEach(path => {
115
+ let { d, el } = path;
75
116
 
76
- // remove zero length linetos
77
- if (removeColinear) pathDataSub = removeZeroLengthLinetos(pathDataSub)
117
+ let pathDataO = parsePathDataNormalized(d, { quadraticToCubic, toAbsolute, arcToCubic });
118
+ //console.log(pathDataO);
78
119
 
79
- // add extremes
80
- //let tMin=0.2, tMax=0.8;
81
- let tMin = 0, tMax = 1;
82
- if (addExtremes) pathDataSub = addExtremePoints(pathDataSub, tMin, tMax)
120
+ // create clone for fallback
121
+ let pathData = JSON.parse(JSON.stringify(pathDataO));
83
122
 
123
+ // count commands for evaluation
124
+ let comCount = pathDataO.length
84
125
 
85
- // sort to top left
86
- if (optimizeOrder) pathDataSub = pathDataToTopLeft(pathDataSub);
126
+ /**
127
+ * get sub paths
128
+ */
129
+ let subPathArr = splitSubpaths(pathData);
87
130
 
88
- // remove colinear/flat
89
- if (removeColinear) pathDataSub = pathDataRemoveColinear(pathDataSub, tolerance, flatBezierToLinetos);
131
+ // cleaned up pathData
132
+ let pathDataArrN = [];
90
133
 
91
- // analyze pathdata to add info about signicant properties such as extremes, corners
92
- let pathDataPlus = analyzePathData(pathDataSub);
134
+ for (let i = 0, l = subPathArr.length; i < l; i++) {
93
135
 
136
+ //let { pathData, bb } = subPathArr[i];
137
+ let pathDataSub = subPathArr[i];
94
138
 
95
- // simplify beziers
96
- let { pathData, bb, dimA } = pathDataPlus;
139
+ // try simplification in reversed order
140
+ if (reverse) pathDataSub = reversePathData(pathDataSub);
97
141
 
142
+ // remove zero length linetos
143
+ if (removeColinear) pathDataSub = removeZeroLengthLinetos(pathDataSub)
98
144
 
145
+ // add extremes
146
+ //let tMin=0.2, tMax=0.8;
147
+ let tMin = 0, tMax = 1;
148
+ if (addExtremes) pathDataSub = addExtremePoints(pathDataSub, tMin, tMax)
99
149
 
100
150
 
151
+ // sort to top left
152
+ if (optimizeOrder) pathDataSub = pathDataToTopLeft(pathDataSub);
101
153
 
102
- let pathDataN = pathData;
154
+ // remove colinear/flat
155
+ if (removeColinear) pathDataSub = pathDataRemoveColinear(pathDataSub, tolerance, flatBezierToLinetos);
103
156
 
157
+ // analyze pathdata to add info about signicant properties such as extremes, corners
158
+ let pathDataPlus = analyzePathData(pathDataSub);
104
159
 
105
160
 
106
-
107
- pathDataN = simplifyBezier ? simplifyPathData(pathDataN, { simplifyBezier, keepInflections, keepExtremes, keepCorners, extrapolateDominant, revertToQuadratics, tolerance, reverse }) : pathDataN;
161
+ // simplify beziers
162
+ let { pathData, bb, dimA } = pathDataPlus;
108
163
 
164
+ //let pathDataN = pathData;
109
165
 
166
+ //console.log(pathDataPlus);
110
167
 
111
- // cubic to arcs
112
- if(cubicToArc){
168
+ pathData = simplifyBezier ? simplifyPathData(pathData, { simplifyBezier, keepInflections, keepExtremes, keepCorners, extrapolateDominant, revertToQuadratics, tolerance, reverse }) : pathData;
113
169
 
114
- let thresh = 3;
115
170
 
116
- pathDataN.forEach((com, c) => {
117
- let { type, values, p0, cp1 = null, cp2 = null, p = null } = com;
118
- if (type === 'C') {
119
- //console.log(com);
120
- let comA = cubicCommandToArc(p0, cp1, cp2, p, thresh)
121
- if(comA.isArc) pathDataN[c] = comA.com;
122
- //if (comQ.type === 'Q') pathDataN[c] = comQ
123
- }
124
- })
171
+ // cubic to arcs
172
+ if (cubicToArc) {
173
+
174
+ let thresh = 3;
125
175
 
126
- // combine adjacent cubics
127
- pathDataN = combineArcs(pathDataN)
176
+ pathData.forEach((com, c) => {
177
+ let { type, values, p0, cp1 = null, cp2 = null, p = null } = com;
178
+ if (type === 'C') {
179
+ //console.log(com);
180
+ let comA = cubicCommandToArc(p0, cp1, cp2, p, thresh)
181
+ if (comA.isArc) pathData[c] = comA.com;
182
+ //if (comQ.type === 'Q') pathDataN[c] = comQ
183
+ }
184
+ })
128
185
 
186
+ // combine adjacent cubics
187
+ pathData = combineArcs(pathData)
188
+
189
+ }
190
+
191
+
192
+ // simplify to quadratics
193
+ if (revertToQuadratics) {
194
+ pathData.forEach((com, c) => {
195
+ let { type, values, p0, cp1 = null, cp2 = null, p = null } = com;
196
+ if (type === 'C') {
197
+ //console.log(com);
198
+ let comQ = revertCubicQuadratic(p0, cp1, cp2, p)
199
+ if (comQ.type === 'Q') pathData[c] = comQ
200
+ }
201
+ })
202
+ }
203
+
204
+
205
+ // update
206
+ pathDataArrN.push(pathData)
129
207
  }
130
208
 
131
209
 
132
- // simplify to quadratics
133
- if (revertToQuadratics) {
134
- pathDataN.forEach((com, c) => {
135
- let { type, values, p0, cp1 = null, cp2 = null, p = null } = com;
136
- if (type === 'C') {
137
- //console.log(com);
138
- let comQ = revertCubicQuadratic(p0, cp1, cp2, p)
139
- if (comQ.type === 'Q') pathDataN[c] = comQ
140
- }
141
- })
210
+ // flatten compound paths
211
+ pathData = pathDataArrN.flat();
212
+
213
+ /**
214
+ * detect accuracy
215
+ */
216
+ if (autoAccuracy) {
217
+ decimals = detectAccuracy(pathData)
142
218
  }
143
219
 
144
220
 
221
+ // optimize
222
+ let pathOptions = {
223
+ toRelative,
224
+ toShorthands,
225
+ decimals,
226
+ }
145
227
 
146
228
 
147
- // update
148
- pathDataArrN.push(pathDataN)
149
- }
229
+ // optimize path data
230
+ pathData = convertPathData(pathData, pathOptions)
150
231
 
151
232
 
152
- // merge pathdata
153
- let pathDataFlat = pathDataArrN.flat();
233
+ // remove zero-length segments introduced by rounding
234
+ let pathDataOpt = []
154
235
 
155
- /**
156
- * detect accuracy
157
- */
158
- if (autoAccuracy) {
159
- decimals = detectAccuracy(pathDataFlat)
160
- }
236
+ pathData.forEach((com, i) => {
237
+ let { type, values } = com;
238
+ if (type === 'l' || type === 'v' || type === 'h') {
239
+ let hasLength = type === 'l' ? (values.join('') !== '00') : values[0] !== 0
240
+ if (hasLength) pathDataOpt.push(com)
241
+ } else {
242
+ pathDataOpt.push(com)
243
+ }
244
+ })
161
245
 
246
+ pathData = pathDataOpt;
162
247
 
163
248
 
164
- // compare command count
165
- let comCountS = pathDataFlat.length
249
+ // compare command count
250
+ let comCountS = pathData.length
166
251
 
167
- // optimize
168
- let pathOptions = {
169
- toRelative,
170
- toShorthands,
171
- decimals,
172
- }
252
+ let dOpt = pathDataToD(pathData, minifyD)
253
+ svgSizeOpt = new Blob([dOpt]).size;
254
+ //compression = +(100/svgSize * (svgSize - svgSizeOpt)).toFixed(2)
255
+ compression = +(100 / svgSize * (svgSizeOpt)).toFixed(2)
256
+
257
+
258
+ path.d = dOpt
259
+ path.report = {
260
+ original: comCount,
261
+ new: comCountS,
262
+ saved: comCount - comCountS,
263
+ compression,
264
+ decimals,
265
+ //success: comCountS < comCount
266
+ }
173
267
 
268
+ // apply new path for svgs
269
+ if (el) el.setAttribute('d', dOpt)
174
270
 
175
- // optimize path data
176
- pathData = convertPathData(pathDataFlat, pathOptions)
177
- let dOpt = pathDataToD(pathData, minifyD)
271
+ });
272
+
273
+ // stringify new SVG
274
+ if (mode) {
275
+ svg = new XMLSerializer().serializeToString(svg);
276
+ svgSizeOpt = new Blob([svg]).size
277
+ //compression = +(100/svgSize * (svgSize-svgSizeOpt)).toFixed(2)
278
+ compression = +(100 / svgSize * (svgSizeOpt)).toFixed(2)
279
+
280
+ svgSize = +(svgSize / 1024).toFixed(3)
281
+ svgSizeOpt = +(svgSizeOpt / 1024).toFixed(3)
282
+
283
+ report = {
284
+ svgSize,
285
+ svgSizeOpt,
286
+ compression
287
+ }
178
288
 
179
- let report = {
180
- original: comCount,
181
- new: comCountS,
182
- saved: comCount - comCountS,
183
- decimals,
184
- success: comCountS < comCount
289
+ } else {
290
+ ({ d, report } = paths[0]);
185
291
  }
186
292
 
187
- return { pathData, d: dOpt, report };
188
293
 
294
+ return !getObject ? (d ? d : svg) : { svg, d, report, inputType, mode };
189
295
 
190
296
  }
191
297
 
@@ -221,9 +327,9 @@ function simplifyPathData(pathData, {
221
327
 
222
328
  // cannot be combined as crossing extremes or corners
223
329
  if (
224
- (keepInflections && isDirChangeN) ||
225
- (keepCorners && corner) ||
226
- (!isDirChange && keepExtremes && extreme)
330
+ (keepInflections && isDirChangeN) ||
331
+ (keepCorners && corner) ||
332
+ (!isDirChange && keepExtremes && extreme)
227
333
  ) {
228
334
  //renderPoint(markers, p, 'red', '1%')
229
335
  pathDataN.push(com)
@@ -0,0 +1,32 @@
1
+ /**
2
+ * get viewBox
3
+ * either from explicit attribute or
4
+ * width and height attributes
5
+ */
6
+
7
+ export function getViewBox(svg = null, round = false) {
8
+
9
+ // browser default
10
+ if (!svg) return { x: 0, y: 0, width: 300, height: 150 }
11
+
12
+ let style = window.getComputedStyle(svg);
13
+
14
+ // the baseVal API method also converts physical units to pixels/user-units
15
+ let w = svg.hasAttribute('width') ? svg.width.baseVal.value : parseFloat(style.width) || 300;
16
+ let h = svg.hasAttribute('height') ? svg.height.baseVal.value : parseFloat(style.height) || 150;
17
+
18
+ let viewBox = svg.getAttribute('viewBox') ? svg.viewBox.baseVal : { x: 0, y: 0, width: w, height: h };
19
+
20
+ // remove SVG constructor
21
+ let { x, y, width, height } = viewBox;
22
+ viewBox = { x, y, width, height };
23
+
24
+ // round to integers
25
+ if (round) {
26
+ for (let prop in viewBox) {
27
+ viewBox[prop] = Math.ceil(viewBox[prop]);
28
+ }
29
+ }
30
+
31
+ return viewBox
32
+ }
@@ -52,7 +52,7 @@ export function checkLineIntersection(p1, p2, p3, p4, exact = true) {
52
52
  y: p1.y + (a * (p2.y - p1.y))
53
53
  }
54
54
 
55
- // console.log('intersectionPoint', intersectionPoint, p1, p2);
55
+ // console.log('intersectionPoint', intersectionPoint, p1, p2);
56
56
 
57
57
 
58
58
 
@@ -985,16 +985,63 @@ export function commandIsFlat(points, tolerance = 0.025) {
985
985
  }
986
986
 
987
987
 
988
+ export function checkBezierFlatness(p0, cpts, p) {
988
989
 
990
+ let isFlat = false;
991
+
992
+ let isCubic = cpts.length===2;
993
+
994
+ let cp1 = cpts[0]
995
+ let cp2 = isCubic ? cpts[1] : cp1;
996
+
997
+ if(p0.x===cp1.x && p0.y===cp1.y && p.x===cp2.x && p.y===cp2.y) return true;
998
+
999
+ let dx1 = cp1.x - p0.x;
1000
+ let dy1 = cp1.y - p0.y;
1001
+
1002
+ let dx2 = p.x - cp2.x;
1003
+ let dy2 = p.y - cp2.y;
1004
+
1005
+ let cross1 = Math.abs(dx1 * dy2 - dy1 * dx2);
1006
+
1007
+ if(!cross1) return true
1008
+
1009
+ let dx0 = p.x - p0.x;
1010
+ let dy0 = p.y - p0.y;
1011
+ let cross0 = Math.abs(dx0 * dy1 - dy0 * dx1);
1012
+
1013
+ if(!cross0) return true
1014
+
1015
+ //let diff = Math.abs(cross0 - cross1)
1016
+ //let rat0 = 1/cross0 * diff;
1017
+ let rat = (cross0/cross1)
1018
+
1019
+ if (rat<1.1 ) {
1020
+ //console.log('cross', cross0, cross1, 'rat', rat, rat0, diff );
1021
+ isFlat = true;
1022
+ }
1023
+
1024
+ return isFlat;
1025
+
1026
+ }
989
1027
 
990
1028
  /**
991
1029
  * sloppy distance calculation
992
1030
  * based on x/y differences
993
1031
  */
994
1032
  export function getDistAv(pt1, pt2) {
995
- let diffX = Math.abs(pt1.x - pt2.x);
996
- let diffY = Math.abs(pt1.y - pt2.y);
1033
+
1034
+ let diffX = Math.abs(pt2.x - pt1.x);
1035
+ let diffY = Math.abs(pt2.y - pt1.y);
997
1036
  let diff = (diffX + diffY) / 2;
1037
+
1038
+ /*
1039
+ let diffX = pt2.x - pt1.x;
1040
+ let diffY = pt2.y - pt1.y;
1041
+ let diff = Math.abs(diffX + diffY) / 2;
1042
+ */
1043
+
1044
+
998
1045
  return diff;
999
1046
  }
1000
1047
 
@@ -1067,7 +1114,7 @@ export function reducePoints(points, maxPoints = 48) {
1067
1114
  }
1068
1115
 
1069
1116
 
1070
- export function mirrorCpts(cpt2_0, pt0, cpt2, pt1, outgoing = true, t=0.666) {
1117
+ export function mirrorCpts(cpt2_0, pt0, cpt2, pt1, outgoing = true, t = 0.666) {
1071
1118
 
1072
1119
  // hypotenuse angle
1073
1120
  let ang0 = getAngle(pt0, pt1, true);
@@ -1,4 +1,4 @@
1
- import { getSquareDistance } from "./geometry.js";
1
+ import { checkBezierFlatness, getDistAv, getSquareDistance } from "./geometry.js";
2
2
  import { getPolygonArea } from "./geometry_area.js";
3
3
  import { renderPoint } from "./visualize.js";
4
4
 
@@ -17,49 +17,62 @@ export function pathDataRemoveColinear(pathData, tolerance = 1, flatBezierToLine
17
17
  let comPrev = pathData[c - 1];
18
18
  let com = pathData[c];
19
19
  let comN = pathData[c + 1] || pathData[l - 1];
20
- let p1 = comN.type === 'Z' ? M : { x: comN.values[comN.values.length - 2], y: comN.values[comN.values.length - 1] }
20
+ let p1 = comN.type.toLowerCase() === 'z' ? M : { x: comN.values[comN.values.length - 2], y: comN.values[comN.values.length - 1] }
21
21
 
22
22
  let { type, values } = com;
23
23
  let valsL = values.slice(-2)
24
24
  p = type !== 'Z' ? { x: valsL[0], y: valsL[1] } : M;
25
25
 
26
- let cpts = type === 'C' ?
27
- [{ x: values[0], y: values[1] }, { x: values[2], y: values[3] }] :
28
- (type === 'Q' ? [{ x: values[0], y: values[1] }] : []);
26
+ let area = getPolygonArea([p0, p, p1], true)
29
27
 
28
+ let distSquare = getSquareDistance(p0, p1)
29
+ let distMax = distSquare / 100 * tolerance
30
30
 
31
- let area = getPolygonArea([p0, ...cpts, p, p1], true)
32
- let distSquare = getSquareDistance(p0, p)
33
- let distMax = distSquare / 500 * tolerance
31
+ let isFlat = area < distMax;
32
+ let isFlatBez = false;
34
33
 
35
- let isFlat = area < distMax
36
-
37
- if(!flatBezierToLinetos && type==='C') isFlat = false;
38
- //let isFlat = flatBezierToLinetos && type === 'C' ? area < distMax : false
39
34
 
35
+ if (!flatBezierToLinetos && type === 'C') isFlat = false;
36
+ //let isFlat = flatBezierToLinetos && type === 'C' ? area < distMax : false
40
37
 
41
38
  // convert flat beziers to linetos
42
- if (flatBezierToLinetos && type === 'C') {
39
+ if (flatBezierToLinetos && (type === 'C' || type === 'Q')) {
40
+
41
+ let cpts = type === 'C' ?
42
+ [{ x: values[0], y: values[1] }, { x: values[2], y: values[3] }] :
43
+ (type === 'Q' ? [{ x: values[0], y: values[1] }] : []);
43
44
 
44
- let areaBez = getPolygonArea([p0, ...cpts, p], true)
45
- let isFlatBez = areaBez < distSquare / 1000
46
45
 
47
- if (isFlatBez && comPrev.type !== 'C') {
46
+ //let areaBez = getPolygonArea([p0, ...cpts, p], true)
47
+
48
+ isFlatBez = checkBezierFlatness(p0, cpts, p)
49
+ // console.log();
50
+
51
+ //isFlatBez = areaBez < distMax * 0.25
52
+ //console.log('isFlatBez', isFlatBez);
53
+ //isFlatBez = false
54
+
55
+ //&& comPrev.type !== 'C'
56
+ if (isFlatBez && c < l - 1 && comPrev.type !== 'C') {
57
+ type = "L"
48
58
  com.type = "L"
49
59
  com.values = valsL
60
+
61
+ //renderPoint(markers, p)
50
62
  }
51
63
 
52
64
  }
53
65
 
54
-
55
66
  // update end point
56
67
  p0 = p;
57
68
 
58
69
  // colinear – exclude arcs (as always =) as semicircles won't have an area
59
- if (type !== 'A' && isFlat && c < l - 1) {
70
+ if ( isFlat && c < l - 1 && (type === 'L' || (flatBezierToLinetos && isFlatBez))) {
71
+ //console.log(area,distMax );
60
72
  continue;
61
73
  }
62
74
 
75
+
63
76
  if (type === 'M') {
64
77
  M = p
65
78
  p0 = M
@@ -7,8 +7,42 @@ import { renderPoint, renderPath } from './visualize.js';
7
7
  import { getPolygonArea } from './geometry_area.js';
8
8
 
9
9
 
10
+ export function pathDataToTopLeft(pathData) {
10
11
 
11
- export function pathDataToTopLeft(pathData, removeFinalLineto = false, reorder = true) {
12
+ let len = pathData.length;
13
+ let isClosed = pathData[len - 1].type.toLowerCase() === 'z'
14
+
15
+ // we can't change starting point for non closed paths
16
+ if (!isClosed) {
17
+ return pathData
18
+ }
19
+
20
+ let newIndex = 0;
21
+
22
+ //get top most index
23
+ let indices = [];
24
+ for (let i = 0; i < len; i++) {
25
+ let com = pathData[i];
26
+ let { type, values } = com;
27
+ let valsLen = values.length
28
+ if (valsLen) {
29
+ let p = { type: type, x: values[valsLen-2], y: values[valsLen-1], index: 0}
30
+ p.index = i
31
+ indices.push(p)
32
+ }
33
+ }
34
+
35
+ // reorder to top left most
36
+ indices = indices.sort((a, b) => +a.y.toFixed(3) - +b.y.toFixed(3) || a.x - b.x);
37
+ newIndex = indices[0].index
38
+
39
+ return newIndex ? shiftSvgStartingPoint(pathData, newIndex) : pathData;
40
+ }
41
+
42
+
43
+
44
+
45
+ export function optimizeClosePath(pathData, removeFinalLineto = false, reorder = true) {
12
46
 
13
47
  let pathDataNew = [];
14
48
  let len = pathData.length;
@@ -21,18 +55,18 @@ export function pathDataToTopLeft(pathData, removeFinalLineto = false, reorder =
21
55
  // check if order is ideal
22
56
  let penultimateCom = pathData[len - 2];
23
57
  let penultimateType = penultimateCom.type;
24
- let penultimateComCoords = penultimateCom.values.slice(-2).map(val=>+val.toFixed(8))
58
+ let penultimateComCoords = penultimateCom.values.slice(-2).map(val => +val.toFixed(8))
25
59
 
26
60
  // last L command ends at M
27
- let isClosingCommand = penultimateComCoords[0] === M.x && penultimateComCoords[1] === M.y
61
+ let isClosingCommand = penultimateComCoords[0] === M.x && penultimateComCoords[1] === M.y
28
62
 
29
63
  // if last segment is not closing or a lineto
30
- let skipReorder = pathData[1].type!=='L' && (!isClosingCommand || penultimateType==='L' )
31
- skipReorder=false
64
+ let skipReorder = pathData[1].type !== 'L' && (!isClosingCommand || penultimateType === 'L')
65
+ skipReorder = false
32
66
 
33
67
 
34
68
  // we can't change starting point for non closed paths
35
- if (!isClosed ) {
69
+ if (!isClosed) {
36
70
  return pathData
37
71
  }
38
72
 
@@ -62,15 +96,15 @@ export function pathDataToTopLeft(pathData, removeFinalLineto = false, reorder =
62
96
  // find top most lineto
63
97
 
64
98
  if (linetos.length) {
65
- let curveAfterLine = indices.filter(com => (com.type !== 'L' && com.type !== 'M') && com.prevCom &&
66
- com.prevCom === 'L' || com.prevCom==='M' && penultimateType==='L' ).sort((a, b) => a.y - b.y || a.x-b.x)[0]
99
+ let curveAfterLine = indices.filter(com => (com.type !== 'L' && com.type !== 'M') && com.prevCom &&
100
+ com.prevCom === 'L' || com.prevCom === 'M' && penultimateType === 'L').sort((a, b) => a.y - b.y || a.x - b.x)[0]
67
101
 
68
102
  newIndex = curveAfterLine ? curveAfterLine.index - 1 : 0
69
103
 
70
104
  }
71
105
  // use top most command
72
106
  else {
73
- indices = indices.sort((a, b) => +a.y.toFixed(1) - +b.y.toFixed(1) || a.x - b.x );
107
+ indices = indices.sort((a, b) => +a.y.toFixed(1) - +b.y.toFixed(1) || a.x - b.x);
74
108
  newIndex = indices[0].index
75
109
  }
76
110