svg-path-simplify 0.4.2 → 0.4.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.
- package/CHANGELOG.md +21 -0
- package/README.md +7 -4
- package/dist/svg-path-simplify.esm.js +3593 -1279
- package/dist/svg-path-simplify.esm.min.js +2 -2
- package/dist/svg-path-simplify.js +3594 -1278
- package/dist/svg-path-simplify.min.js +2 -2
- package/dist/svg-path-simplify.pathdata.esm.js +1017 -538
- package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
- package/dist/svg-path-simplify.poly.cjs +9 -8
- package/docs/privacy-webapp.md +24 -0
- package/index.html +331 -152
- package/package.json +1 -1
- package/src/constants.js +4 -0
- package/src/css_parse.js +317 -0
- package/src/detect_input.js +76 -28
- package/src/index.js +8 -0
- package/src/pathData_simplify_cubic.js +26 -16
- package/src/pathData_simplify_harmonize_cpts.js +77 -1
- package/src/pathData_simplify_revertToquadratics.js +0 -1
- package/src/pathSimplify-main.js +304 -276
- package/src/pathSimplify-only-pathdata.js +7 -2
- package/src/pathSimplify-presets.js +254 -0
- package/src/poly-fit-curve-schneider.js +14 -7
- package/src/simplify_poly_RC.js +102 -0
- package/src/simplify_poly_RDP.js +109 -1
- package/src/simplify_poly_radial_distance.js +3 -3
- package/src/string_helpers.js +130 -4
- package/src/svg-getAttributes.js +4 -2
- package/src/svgii/convert_units.js +1 -1
- package/src/svgii/geometry.js +322 -5
- package/src/svgii/geometry_bbox_element.js +1 -1
- package/src/svgii/geometry_deduceRadius.js +116 -27
- package/src/svgii/geometry_length.js +253 -0
- package/src/svgii/pathData_analyze.js +18 -0
- package/src/svgii/pathData_convert.js +193 -89
- package/src/svgii/pathData_fix_directions.js +12 -14
- package/src/svgii/pathData_fromPoly.js +3 -3
- package/src/svgii/pathData_getLength.js +86 -0
- package/src/svgii/pathData_parse.js +2 -0
- package/src/svgii/pathData_parse_els.js +66 -68
- package/src/svgii/pathData_reorder.js +122 -16
- package/src/svgii/pathData_simplify_refineCorners.js +130 -35
- package/src/svgii/pathData_simplify_refine_round.js +420 -0
- package/src/svgii/pathData_split_to_groups.js +168 -0
- package/src/svgii/pathData_stringify.js +26 -64
- package/src/svgii/pathData_toPolygon.js +3 -4
- package/src/svgii/poly_analyze.js +61 -0
- package/src/svgii/poly_normalize.js +11 -2
- package/src/svgii/poly_to_pathdata.js +85 -24
- package/src/svgii/rounding.js +80 -78
- package/src/svgii/svg_cleanup.js +421 -619
- package/src/svgii/svg_cleanup_convertPathLength.js +39 -0
- package/src/svgii/svg_cleanup_general_svg_atts.js +97 -0
- package/src/svgii/svg_cleanup_normalize_transforms.js +83 -0
- package/src/svgii/svg_cleanup_remove_els_and_atts.js +77 -0
- package/src/svgii/svg_cleanup_ungroup.js +36 -0
- package/src/svgii/svg_el_parse_style_props.js +72 -47
- package/src/svgii/svg_getElementLength.js +67 -0
- package/src/svgii/svg_validate.js +220 -0
- package/tests/testSVG.js +14 -1
- package/src/svgii/pathData_refine_round.js +0 -222
|
@@ -16,29 +16,25 @@ import { autoRound, roundTo } from './rounding.js';
|
|
|
16
16
|
import { attLookup } from './svg-styles-to-attributes-const.js';
|
|
17
17
|
import { qrDecomposeMatrix } from './transform_qr_decompose.js';
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Convert shapes to paths
|
|
21
|
+
* converts also transforms
|
|
22
|
+
*/
|
|
20
23
|
export function shapeElToPath(el, { width = 0,
|
|
21
24
|
height = 0,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
convert_poly = false,
|
|
25
|
-
convert_lines = false,
|
|
26
|
-
//matrix={a:1, b:0, c:0, d:1, e:0, f:0},
|
|
27
|
-
matrix=null
|
|
25
|
+
convertShapes = [],
|
|
26
|
+
matrix = null
|
|
28
27
|
|
|
29
28
|
} = {}) {
|
|
30
29
|
|
|
30
|
+
|
|
31
31
|
let nodeName = el.nodeName.toLowerCase();
|
|
32
32
|
//console.log('shapeElToPath', nodeName);
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
(nodeName === 'circle' || nodeName === 'ellipse') && !convert_ellipses ||
|
|
39
|
-
(nodeName === 'polygon' || nodeName === 'polyline') && !convert_poly ||
|
|
40
|
-
(nodeName === 'line') && !convert_lines
|
|
41
|
-
) return el;
|
|
35
|
+
|
|
36
|
+
if (!convertShapes.includes(nodeName)) return el;
|
|
37
|
+
//console.log(convertShapes);
|
|
42
38
|
|
|
43
39
|
|
|
44
40
|
let pathData = getPathDataFromEl(el, { width, height });
|
|
@@ -47,8 +43,9 @@ export function shapeElToPath(el, { width = 0,
|
|
|
47
43
|
let exclude = ['d', 'x', 'y', 'x1', 'y1', 'x2', 'y2', 'cx', 'cy', 'dx', 'dy', 'r', 'rx', 'ry', 'width', 'height', 'points'];
|
|
48
44
|
|
|
49
45
|
// transform pathData
|
|
50
|
-
if(matrix && Object.values(matrix).join('')!=='100100'){
|
|
46
|
+
if (matrix && Object.values(matrix).join('') !== '100100') {
|
|
51
47
|
pathData = transformPathData(pathData, matrix)
|
|
48
|
+
//console.log('transformPathData', pathData);
|
|
52
49
|
exclude.push('transform', 'transform-origin')
|
|
53
50
|
}
|
|
54
51
|
|
|
@@ -72,14 +69,7 @@ export function shapeElToPath(el, { width = 0,
|
|
|
72
69
|
return pathN
|
|
73
70
|
|
|
74
71
|
}
|
|
75
|
-
/*
|
|
76
|
-
export function copyAttributes(newEl, oldEl){
|
|
77
72
|
|
|
78
|
-
let attributes = [...oldEl.attributes].map(att => att.name);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
*/
|
|
83
73
|
|
|
84
74
|
|
|
85
75
|
// retrieve pathdata from svg geometry elements
|
|
@@ -112,39 +102,7 @@ export function getPathDataFromEl(el, {
|
|
|
112
102
|
case 'rect':
|
|
113
103
|
attNames = ['x', 'y', 'width', 'height', 'rx', 'ry'];
|
|
114
104
|
({ x=0, y=0, width=0, height=0, rx=0, ry=0 } = atts);
|
|
115
|
-
|
|
116
|
-
if (!rx && !ry) {
|
|
117
|
-
pathData = [
|
|
118
|
-
{ type: "M", values: [x, y] },
|
|
119
|
-
{ type: "L", values: [x + width, y] },
|
|
120
|
-
{ type: "L", values: [x + width, y + height] },
|
|
121
|
-
{ type: "L", values: [x, y + height] },
|
|
122
|
-
{ type: "Z", values: [] }
|
|
123
|
-
];
|
|
124
|
-
} else {
|
|
125
|
-
|
|
126
|
-
rx = rx ? rx : ry;
|
|
127
|
-
ry = ry ? ry : rx;
|
|
128
|
-
|
|
129
|
-
if (rx > width / 2) {
|
|
130
|
-
rx = width / 2;
|
|
131
|
-
}
|
|
132
|
-
if (ry > height / 2) {
|
|
133
|
-
ry = height / 2;
|
|
134
|
-
}
|
|
135
|
-
pathData = [
|
|
136
|
-
{ type: "M", values: [x + rx, y] },
|
|
137
|
-
{ type: "L", values: [x + width - rx, y] },
|
|
138
|
-
{ type: "A", values: [rx, ry, 0, 0, 1, x + width, y + ry] },
|
|
139
|
-
{ type: "L", values: [x + width, y + height - ry] },
|
|
140
|
-
{ type: "A", values: [rx, ry, 0, 0, 1, x + width - rx, y + height] },
|
|
141
|
-
{ type: "L", values: [x + rx, y + height] },
|
|
142
|
-
{ type: "A", values: [rx, ry, 0, 0, 1, x, y + height - ry] },
|
|
143
|
-
{ type: "L", values: [x, y + ry] },
|
|
144
|
-
{ type: "A", values: [rx, ry, 0, 0, 1, x + rx, y] },
|
|
145
|
-
{ type: "Z", values: [] }
|
|
146
|
-
];
|
|
147
|
-
}
|
|
105
|
+
pathData = rectToPathData(x, y, width, height, rx, ry);
|
|
148
106
|
break;
|
|
149
107
|
|
|
150
108
|
case 'circle':
|
|
@@ -164,7 +122,6 @@ export function getPathDataFromEl(el, {
|
|
|
164
122
|
ry = ry ? ry : r;
|
|
165
123
|
}
|
|
166
124
|
|
|
167
|
-
|
|
168
125
|
// simplified radii for circles
|
|
169
126
|
let rxS = isCircle && r >= 1 ? 1 : rx;
|
|
170
127
|
let ryS = isCircle && r >= 1 ? 1 : ry;
|
|
@@ -210,13 +167,51 @@ export function getPathDataFromEl(el, {
|
|
|
210
167
|
};
|
|
211
168
|
|
|
212
169
|
|
|
170
|
+
export function rectToPathData(x = 0, y = 0, width = 0, height = 0, rx = 0, ry = 0) {
|
|
171
|
+
let pathData = [];
|
|
172
|
+
|
|
173
|
+
if (!rx && !ry) {
|
|
174
|
+
pathData = [
|
|
175
|
+
{ type: "M", values: [x, y] },
|
|
176
|
+
{ type: "L", values: [x + width, y] },
|
|
177
|
+
{ type: "L", values: [x + width, y + height] },
|
|
178
|
+
{ type: "L", values: [x, y + height] },
|
|
179
|
+
{ type: "Z", values: [] }
|
|
180
|
+
];
|
|
181
|
+
} else {
|
|
182
|
+
|
|
183
|
+
rx = rx ? rx : ry;
|
|
184
|
+
ry = ry ? ry : rx;
|
|
185
|
+
|
|
186
|
+
if (rx > width / 2) {
|
|
187
|
+
rx = width / 2;
|
|
188
|
+
}
|
|
189
|
+
if (ry > height / 2) {
|
|
190
|
+
ry = height / 2;
|
|
191
|
+
}
|
|
192
|
+
pathData = [
|
|
193
|
+
{ type: "M", values: [x + rx, y] },
|
|
194
|
+
{ type: "L", values: [x + width - rx, y] },
|
|
195
|
+
{ type: "A", values: [rx, ry, 0, 0, 1, x + width, y + ry] },
|
|
196
|
+
{ type: "L", values: [x + width, y + height - ry] },
|
|
197
|
+
{ type: "A", values: [rx, ry, 0, 0, 1, x + width - rx, y + height] },
|
|
198
|
+
{ type: "L", values: [x + rx, y + height] },
|
|
199
|
+
{ type: "A", values: [rx, ry, 0, 0, 1, x, y + height - ry] },
|
|
200
|
+
{ type: "L", values: [x, y + ry] },
|
|
201
|
+
{ type: "A", values: [rx, ry, 0, 0, 1, x + rx, y] },
|
|
202
|
+
{ type: "Z", values: [] }
|
|
203
|
+
];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return pathData
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
|
|
213
211
|
|
|
214
212
|
|
|
215
213
|
export function pathElToShape(el, {
|
|
216
|
-
|
|
217
|
-
convert_ellipses = false,
|
|
218
|
-
convert_poly = false,
|
|
219
|
-
convert_lines = false
|
|
214
|
+
convertShapes = [],
|
|
220
215
|
} = {}) {
|
|
221
216
|
|
|
222
217
|
//console.log('pathElToShape', convert_rects, convert_ellipses, convert_lines );
|
|
@@ -236,10 +231,13 @@ export function pathElToShape(el, {
|
|
|
236
231
|
let attsNew = {}
|
|
237
232
|
let decimals = 7;
|
|
238
233
|
|
|
234
|
+
|
|
239
235
|
if (isPoly) {
|
|
240
236
|
|
|
237
|
+
//console.log('pathsToShapes', isPoly);
|
|
238
|
+
|
|
241
239
|
// is line
|
|
242
|
-
if (pathData.length === 2 &&
|
|
240
|
+
if (pathData.length === 2 && convertShapes.includes('line')) {
|
|
243
241
|
type = 'line'
|
|
244
242
|
shape = document.createElementNS(svgNs, type)
|
|
245
243
|
let [x1, y1, x2, y2] = [...pathData[0].values, ...pathData[1].values].map(val => roundTo(val, decimals))
|
|
@@ -255,7 +253,7 @@ export function pathElToShape(el, {
|
|
|
255
253
|
let areaDiff = Math.abs(1 - areaRect / areaPoly);
|
|
256
254
|
|
|
257
255
|
// is rect
|
|
258
|
-
if (
|
|
256
|
+
if (convertShapes.includes('rect') && areaDiff < 0.01) {
|
|
259
257
|
type = 'rect'
|
|
260
258
|
shape = document.createElementNS(svgNs, type)
|
|
261
259
|
let { x, y, width, height } = bb
|
|
@@ -263,7 +261,7 @@ export function pathElToShape(el, {
|
|
|
263
261
|
|
|
264
262
|
}
|
|
265
263
|
// polyline or polygon
|
|
266
|
-
else if(
|
|
264
|
+
else if (convertShapes.includes('polygon') || convertShapes.includes('polyline')) {
|
|
267
265
|
type = closed ? 'polygon' : 'polyline';
|
|
268
266
|
shape = document.createElementNS(svgNs, type)
|
|
269
267
|
let points = vertices.map(pt => { return [pt.x, pt.y] }).flat().map(val => roundTo(val, decimals)).join(' ')
|
|
@@ -272,7 +270,7 @@ export function pathElToShape(el, {
|
|
|
272
270
|
}
|
|
273
271
|
}
|
|
274
272
|
// circles or ellipses
|
|
275
|
-
else if (!hasLines &&
|
|
273
|
+
else if (!hasLines && (convertShapes.includes('circle') || convertShapes.includes('ellipse'))) {
|
|
276
274
|
|
|
277
275
|
// try to convert cubics to arcs
|
|
278
276
|
if (!hasArcs && hasBeziers) {
|
|
@@ -308,11 +306,11 @@ export function pathElToShape(el, {
|
|
|
308
306
|
rxVals = Array.from(rxVals)
|
|
309
307
|
ryVals = Array.from(ryVals)
|
|
310
308
|
|
|
311
|
-
if(cxVals.length===1 && cyVals.length===1 && rxVals.length===1 && ryVals.length===1){
|
|
309
|
+
if (cxVals.length === 1 && cyVals.length === 1 && rxVals.length === 1 && ryVals.length === 1) {
|
|
312
310
|
let [rx, ry, cx, cy] = [rxVals[0], ryVals[0], cxVals[0], cyVals[0]]
|
|
313
|
-
type = rx===ry ? 'circle' : 'ellipse';
|
|
311
|
+
type = rx === ry ? 'circle' : 'ellipse';
|
|
314
312
|
shape = document.createElementNS(svgNs, type)
|
|
315
|
-
attsNew = type==='circle' ? { r:rx, cx, cy } : {rx, ry, cx, cy}
|
|
313
|
+
attsNew = type === 'circle' ? { r: rx, cx, cy } : { rx, ry, cx, cy }
|
|
316
314
|
}
|
|
317
315
|
}
|
|
318
316
|
}
|
|
@@ -334,11 +332,11 @@ export function pathElToShape(el, {
|
|
|
334
332
|
shape.setAttribute(att, attributes[att])
|
|
335
333
|
}
|
|
336
334
|
}
|
|
337
|
-
|
|
338
335
|
// replace
|
|
339
336
|
el = shape;
|
|
340
337
|
}
|
|
341
338
|
|
|
339
|
+
//console.log(el);
|
|
342
340
|
return el;
|
|
343
341
|
|
|
344
342
|
}
|
|
@@ -14,7 +14,6 @@ export function pathDataToTopLeft(pathData) {
|
|
|
14
14
|
let len = pathData.length;
|
|
15
15
|
let isClosed = pathData[len - 1].type.toLowerCase() === 'z'
|
|
16
16
|
|
|
17
|
-
//return pathData;
|
|
18
17
|
|
|
19
18
|
// we can't change starting point for non closed paths
|
|
20
19
|
if (!isClosed) {
|
|
@@ -30,7 +29,7 @@ export function pathDataToTopLeft(pathData) {
|
|
|
30
29
|
let { type, values } = com;
|
|
31
30
|
let valsLen = values.length
|
|
32
31
|
if (valsLen) {
|
|
33
|
-
let p = { type: type, x: values[valsLen-2], y: values[valsLen-1], index: 0}
|
|
32
|
+
let p = { type: type, x: values[valsLen - 2], y: values[valsLen - 1], index: 0 }
|
|
34
33
|
p.index = i
|
|
35
34
|
indices.push(p)
|
|
36
35
|
}
|
|
@@ -38,16 +37,124 @@ export function pathDataToTopLeft(pathData) {
|
|
|
38
37
|
|
|
39
38
|
// reorder to top left most
|
|
40
39
|
//|| a.x - b.x
|
|
41
|
-
indices = indices.sort((a, b) => +a.y.toFixed(8) - +b.y.toFixed(8) || a.x-b.x
|
|
40
|
+
indices = indices.sort((a, b) => +a.y.toFixed(8) - +b.y.toFixed(8) || a.x - b.x);
|
|
42
41
|
newIndex = indices[0].index
|
|
43
42
|
|
|
44
|
-
return
|
|
43
|
+
return newIndex ? shiftSvgStartingPoint(pathData, newIndex) : pathData;
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
|
|
47
|
+
export function optimizeClosePath(pathData, { removeFinalLineto = true, autoClose = true } = {}) {
|
|
48
48
|
|
|
49
|
+
let pathDataN = pathData;
|
|
50
|
+
let l = pathData.length;
|
|
51
|
+
let M = { x: +pathData[0].values[0].toFixed(8), y: +pathData[0].values[1].toFixed(8) }
|
|
52
|
+
let isClosed = pathData[l - 1].type.toLowerCase() === 'z'
|
|
53
|
+
//let linetos = pathData.filter(com => com.type === 'L')
|
|
54
|
+
//let hasLinetos = linetos.length;
|
|
55
|
+
let hasLinetos = false
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
// check if path is closed by explicit lineto
|
|
59
|
+
let idxPenultimate = isClosed ? l - 2 : l - 1
|
|
60
|
+
let penultimateCom = pathData[idxPenultimate];
|
|
61
|
+
let penultimateType = penultimateCom.type;
|
|
62
|
+
let penultimateComCoords = penultimateCom.values.slice(-2).map(val => +val.toFixed(8))
|
|
63
|
+
|
|
64
|
+
// last L command ends at M
|
|
65
|
+
let hasClosingCommand = penultimateComCoords[0] === M.x && penultimateComCoords[1] === M.y
|
|
66
|
+
let lastIsLine = penultimateType === 'L'
|
|
67
|
+
//console.log(pathData);
|
|
68
|
+
|
|
69
|
+
// create index
|
|
70
|
+
let indices = [];
|
|
71
|
+
for (let i = 0; i < l; i++) {
|
|
72
|
+
let com = pathData[i];
|
|
73
|
+
let { type, values, p0, p } = com;
|
|
74
|
+
|
|
75
|
+
if(type==='L') hasLinetos = true;
|
|
76
|
+
|
|
77
|
+
// exclude Z
|
|
78
|
+
if (values.length) {
|
|
79
|
+
let valsL = values.slice(-2)
|
|
80
|
+
|
|
81
|
+
let x = Math.min(p0.x, p.x)
|
|
82
|
+
let y = Math.min(p0.y, p.y)
|
|
83
|
+
|
|
84
|
+
let prevCom = pathData[i - 1] ? pathData[i - 1] : pathData[idxPenultimate]
|
|
85
|
+
let prevType = prevCom.type
|
|
86
|
+
//let p = { type: type, x: valsL[0], y: valsL[1], dist: 0, index: 0, prevType }
|
|
87
|
+
let item = { type: type, x, y, index: 0, prevType }
|
|
88
|
+
item.index = i
|
|
89
|
+
indices.push(item)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let xMin = Infinity;
|
|
95
|
+
let yMin = Infinity;
|
|
96
|
+
let idx_top = null;
|
|
97
|
+
let len = indices.length
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
for (let i = 0; i < len; i++) {
|
|
101
|
+
let com = indices[i];
|
|
102
|
+
let { type, index, x, y, prevType } = com;
|
|
103
|
+
|
|
104
|
+
if (hasLinetos && prevType === 'L') {
|
|
105
|
+
if (x < xMin && y < yMin) {
|
|
106
|
+
idx_top = index-1;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (y < yMin) {
|
|
110
|
+
yMin = y
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (x < xMin) {
|
|
114
|
+
xMin = x
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
// shift to better starting point
|
|
121
|
+
if (idx_top) {
|
|
122
|
+
pathDataN = shiftSvgStartingPoint(pathDataN, idx_top)
|
|
123
|
+
|
|
124
|
+
// update penultimate - reorder might have added new close paths
|
|
125
|
+
l = pathDataN.length
|
|
126
|
+
M = { x: +pathDataN[0].values[0].toFixed(8), y: +pathDataN[0].values[1].toFixed(8) }
|
|
127
|
+
|
|
128
|
+
idxPenultimate = isClosed ? l - 2 : l - 1
|
|
129
|
+
penultimateCom = pathDataN[idxPenultimate];
|
|
130
|
+
penultimateType = penultimateCom.type;
|
|
131
|
+
penultimateComCoords = penultimateCom.values.slice(-2).map(val => +val.toFixed(8))
|
|
132
|
+
lastIsLine = penultimateType ==='L'
|
|
133
|
+
|
|
134
|
+
// last L command ends at M
|
|
135
|
+
hasClosingCommand = penultimateComCoords[0] === M.x && penultimateComCoords[1] === M.y
|
|
136
|
+
|
|
137
|
+
}
|
|
49
138
|
|
|
50
|
-
|
|
139
|
+
|
|
140
|
+
// remove unnecessary closing lineto
|
|
141
|
+
if (removeFinalLineto && hasClosingCommand && lastIsLine) {
|
|
142
|
+
pathDataN.splice(l - 2, 1)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// add close path
|
|
146
|
+
if (autoClose && !isClosed && hasClosingCommand) {
|
|
147
|
+
pathDataN.push({ type: 'Z', values: [] })
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return pathDataN
|
|
151
|
+
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
export function optimizeClosePath__(pathData, { removeFinalLineto = true, autoClose = true } = {}) {
|
|
51
158
|
|
|
52
159
|
let pathDataNew = [];
|
|
53
160
|
let l = pathData.length;
|
|
@@ -58,17 +165,16 @@ export function optimizeClosePath(pathData, {removeFinalLineto = true, autoClose
|
|
|
58
165
|
|
|
59
166
|
|
|
60
167
|
// check if order is ideal
|
|
61
|
-
let idxPenultimate = isClosed ? l-2 : l-1
|
|
62
|
-
|
|
168
|
+
let idxPenultimate = isClosed ? l - 2 : l - 1
|
|
63
169
|
let penultimateCom = pathData[idxPenultimate];
|
|
64
170
|
let penultimateType = penultimateCom.type;
|
|
65
171
|
let penultimateComCoords = penultimateCom.values.slice(-2).map(val => +val.toFixed(8))
|
|
66
172
|
|
|
67
173
|
// last L command ends at M
|
|
68
|
-
let
|
|
174
|
+
let hasClosingCommand = penultimateComCoords[0] === M.x && penultimateComCoords[1] === M.y
|
|
69
175
|
|
|
70
176
|
// add closepath Z to enable order optimizations
|
|
71
|
-
if(!isClosed && autoClose &&
|
|
177
|
+
if (!isClosed && autoClose && hasClosingCommand) {
|
|
72
178
|
|
|
73
179
|
/*
|
|
74
180
|
// adjust final coords
|
|
@@ -77,14 +183,14 @@ export function optimizeClosePath(pathData, {removeFinalLineto = true, autoClose
|
|
|
77
183
|
pathData[idxPenultimate].values[valsLastLen-2] = M.x
|
|
78
184
|
pathData[idxPenultimate].values[valsLastLen-1] = M.y
|
|
79
185
|
*/
|
|
80
|
-
|
|
81
|
-
pathData.push({type:'Z', values:[]})
|
|
186
|
+
|
|
187
|
+
pathData.push({ type: 'Z', values: [] })
|
|
82
188
|
isClosed = true;
|
|
83
189
|
l++
|
|
84
190
|
}
|
|
85
191
|
|
|
86
192
|
// if last segment is not closing or a lineto
|
|
87
|
-
let skipReorder = pathData[1].type !== 'L' && (!
|
|
193
|
+
let skipReorder = pathData[1].type !== 'L' && (!hasClosingCommand || penultimateCom.type === 'L')
|
|
88
194
|
skipReorder = false
|
|
89
195
|
|
|
90
196
|
|
|
@@ -143,13 +249,13 @@ export function optimizeClosePath(pathData, {removeFinalLineto = true, autoClose
|
|
|
143
249
|
// remove last lineto
|
|
144
250
|
penultimateCom = pathData[l - 2];
|
|
145
251
|
penultimateType = penultimateCom.type;
|
|
146
|
-
penultimateComCoords = penultimateCom.values.slice(-2).map(val
|
|
252
|
+
penultimateComCoords = penultimateCom.values.slice(-2).map(val => +val.toFixed(8))
|
|
147
253
|
|
|
148
|
-
|
|
254
|
+
hasClosingCommand = penultimateType === 'L' && penultimateComCoords[0] === M.x && penultimateComCoords[1] === M.y
|
|
149
255
|
|
|
150
|
-
//console.log('penultimateCom',
|
|
256
|
+
//console.log('penultimateCom', hasClosingCommand, penultimateCom.values, M);
|
|
151
257
|
|
|
152
|
-
if (removeFinalLineto &&
|
|
258
|
+
if (removeFinalLineto && hasClosingCommand) {
|
|
153
259
|
pathData.splice(l - 2, 1)
|
|
154
260
|
}
|
|
155
261
|
|