svg-path-simplify 0.4.3 → 0.4.5
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 +2 -1
- package/dist/svg-path-simplify.esm.js +1670 -509
- package/dist/svg-path-simplify.esm.min.js +2 -2
- package/dist/svg-path-simplify.js +1671 -508
- package/dist/svg-path-simplify.min.js +2 -2
- package/dist/svg-path-simplify.pathdata.esm.js +936 -463
- package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
- package/dist/svg-path-simplify.poly.cjs +9 -8
- package/index.html +60 -20
- package/package.json +1 -1
- package/src/constants.js +4 -0
- package/src/detect_input.js +47 -29
- package/src/index.js +8 -0
- package/src/pathData_simplify_cubic.js +46 -18
- package/src/pathData_simplify_revertToquadratics.js +0 -1
- package/src/pathSimplify-main.js +81 -20
- package/src/pathSimplify-only-pathdata.js +7 -2
- package/src/pathSimplify-presets.js +14 -4
- package/src/svg-getAttributes.js +5 -3
- package/src/svgii/convert_units.js +1 -1
- package/src/svgii/geometry.js +140 -2
- package/src/svgii/geometry_bbox_element.js +1 -1
- package/src/svgii/geometry_deduceRadius.js +116 -27
- package/src/svgii/geometry_length.js +18 -2
- package/src/svgii/pathData_analyze.js +18 -0
- package/src/svgii/pathData_convert.js +188 -88
- package/src/svgii/pathData_fix_directions.js +10 -18
- package/src/svgii/pathData_reorder.js +123 -16
- package/src/svgii/pathData_simplify_refineCorners.js +130 -35
- package/src/svgii/pathData_simplify_refine_round.js +420 -0
- package/src/svgii/poly_normalize.js +9 -8
- package/src/svgii/rounding.js +112 -80
- package/src/svgii/svg_cleanup.js +75 -22
- package/src/svgii/svg_cleanup_convertPathLength.js +27 -15
- package/src/svgii/svg_cleanup_normalize_transforms.js +1 -1
- package/src/svgii/svg_cleanup_remove_els_and_atts.js +6 -1
- package/src/svgii/svg_el_parse_style_props.js +13 -10
- package/src/svgii/svg_validate.js +220 -0
- package/tests/testSVG.js +14 -1
- package/src/svgii/pathData_refine_round.js +0 -222
|
@@ -17,54 +17,10 @@ import { pathDataToD } from './pathData_stringify';
|
|
|
17
17
|
import { roundPathData } from './rounding';
|
|
18
18
|
import { renderPoint } from './visualize';
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
toAbsolute = true,
|
|
25
|
-
toLonghands = true,
|
|
26
|
-
|
|
27
|
-
// not necessary unless you need cubics only
|
|
28
|
-
quadraticToCubic = false,
|
|
29
|
-
|
|
30
|
-
// mostly a fallback if arc calculations fail
|
|
31
|
-
arcToCubic = false,
|
|
32
|
-
// arc to cubic precision - adds more segments for better precision
|
|
33
|
-
arcAccuracy = 4,
|
|
34
|
-
} = {}
|
|
35
|
-
) {
|
|
36
|
-
|
|
37
|
-
// is already array
|
|
38
|
-
let isArray = Array.isArray(d);
|
|
39
|
-
|
|
40
|
-
// normalize native pathData to regular array
|
|
41
|
-
let hasConstructor = isArray && typeof d[0] === 'object' && typeof d[0].constructor === 'function'
|
|
42
|
-
/*
|
|
43
|
-
if (hasConstructor) {
|
|
44
|
-
d = d.map(com => { return { type: com.type, values: com.values } })
|
|
45
|
-
console.log('hasConstructor', hasConstructor, (typeof d[0].constructor), d);
|
|
46
|
-
}
|
|
47
|
-
*/
|
|
48
|
-
|
|
49
|
-
let pathDataObj = isArray ? d : parsePathDataString(d);
|
|
50
|
-
|
|
51
|
-
let { hasRelatives = true, hasShorthands = true, hasQuadratics = true, hasArcs = true } = pathDataObj;
|
|
52
|
-
let pathData = hasConstructor ? pathDataObj : pathDataObj.pathData;
|
|
53
|
-
|
|
54
|
-
//console.log('???quadraticToCubic', quadraticToCubic);
|
|
55
|
-
|
|
56
|
-
// normalize
|
|
57
|
-
pathData = normalizePathData(pathData,
|
|
58
|
-
{
|
|
59
|
-
toAbsolute, toLonghands, quadraticToCubic, arcToCubic, arcAccuracy,
|
|
60
|
-
hasRelatives, hasShorthands, hasQuadratics, hasArcs
|
|
61
|
-
},
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
return pathData;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
|
|
20
|
+
/**
|
|
21
|
+
* wrapper function for
|
|
22
|
+
* all path data conversion
|
|
23
|
+
*/
|
|
68
24
|
export function convertPathData(pathData, {
|
|
69
25
|
toShorthands = true,
|
|
70
26
|
toLonghands = false,
|
|
@@ -126,23 +82,25 @@ export function convertPathData(pathData, {
|
|
|
126
82
|
//console.log(toShorthands, toRelative, decimals);
|
|
127
83
|
if (hasQuadratics && quadraticToCubic) pathData = pathDataQuadraticToCubic(pathData);
|
|
128
84
|
|
|
129
|
-
if(toMixed) toRelative = true
|
|
85
|
+
if (toMixed) toRelative = true
|
|
130
86
|
|
|
131
87
|
// pre round - before relative conversion to minimize distortions
|
|
132
88
|
if (decimals > -1 && toRelative) pathData = roundPathData(pathData, decimals);
|
|
133
89
|
|
|
134
90
|
// clone absolute pathdata
|
|
135
|
-
if(toMixed){
|
|
91
|
+
if (toMixed) {
|
|
136
92
|
pathDataAbs = JSON.parse(JSON.stringify(pathData))
|
|
137
93
|
}
|
|
138
94
|
|
|
139
95
|
if (toRelative) pathData = pathDataToRelative(pathData);
|
|
96
|
+
|
|
97
|
+
// final rounding
|
|
140
98
|
if (decimals > -1) pathData = roundPathData(pathData, decimals);
|
|
141
99
|
|
|
142
100
|
|
|
143
101
|
// choose most compact commands: relative or absolute
|
|
144
|
-
if(toMixed){
|
|
145
|
-
for(let i=0; i<pathData.length; i++){
|
|
102
|
+
if (toMixed) {
|
|
103
|
+
for (let i = 0; i < pathData.length; i++) {
|
|
146
104
|
let com = pathData[i]
|
|
147
105
|
let comA = pathDataAbs[i]
|
|
148
106
|
// compare Lengths
|
|
@@ -152,7 +110,7 @@ export function convertPathData(pathData, {
|
|
|
152
110
|
let lenR = comStr.length;
|
|
153
111
|
let lenA = comStrA.length;
|
|
154
112
|
|
|
155
|
-
if(lenA<lenR){
|
|
113
|
+
if (lenA < lenR) {
|
|
156
114
|
//console.log('absolute is shorter', comStrA, comStr);
|
|
157
115
|
pathData[i] = pathDataAbs[i]
|
|
158
116
|
}
|
|
@@ -162,6 +120,56 @@ export function convertPathData(pathData, {
|
|
|
162
120
|
return pathData
|
|
163
121
|
}
|
|
164
122
|
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
export function parsePathDataNormalized(d,
|
|
127
|
+
{
|
|
128
|
+
// necessary for most calculations
|
|
129
|
+
toAbsolute = true,
|
|
130
|
+
toLonghands = true,
|
|
131
|
+
|
|
132
|
+
// not necessary unless you need cubics only
|
|
133
|
+
quadraticToCubic = false,
|
|
134
|
+
|
|
135
|
+
// mostly a fallback if arc calculations fail
|
|
136
|
+
arcToCubic = false,
|
|
137
|
+
// arc to cubic precision - adds more segments for better precision
|
|
138
|
+
arcAccuracy = 4,
|
|
139
|
+
} = {}
|
|
140
|
+
) {
|
|
141
|
+
|
|
142
|
+
// is already array
|
|
143
|
+
let isArray = Array.isArray(d);
|
|
144
|
+
|
|
145
|
+
// normalize native pathData to regular array
|
|
146
|
+
let hasConstructor = isArray && typeof d[0] === 'object' && typeof d[0].constructor === 'function'
|
|
147
|
+
/*
|
|
148
|
+
if (hasConstructor) {
|
|
149
|
+
d = d.map(com => { return { type: com.type, values: com.values } })
|
|
150
|
+
console.log('hasConstructor', hasConstructor, (typeof d[0].constructor), d);
|
|
151
|
+
}
|
|
152
|
+
*/
|
|
153
|
+
|
|
154
|
+
let pathDataObj = isArray ? d : parsePathDataString(d);
|
|
155
|
+
|
|
156
|
+
let { hasRelatives = true, hasShorthands = true, hasQuadratics = true, hasArcs = true } = pathDataObj;
|
|
157
|
+
let pathData = hasConstructor ? pathDataObj : pathDataObj.pathData;
|
|
158
|
+
|
|
159
|
+
//console.log('???quadraticToCubic', quadraticToCubic);
|
|
160
|
+
|
|
161
|
+
// normalize
|
|
162
|
+
pathData = normalizePathData(pathData,
|
|
163
|
+
{
|
|
164
|
+
toAbsolute, toLonghands, quadraticToCubic, arcToCubic, arcAccuracy,
|
|
165
|
+
hasRelatives, hasShorthands, hasQuadratics, hasArcs
|
|
166
|
+
},
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
return pathData;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
165
173
|
/**
|
|
166
174
|
*
|
|
167
175
|
* @param {*} pathData
|
|
@@ -170,50 +178,100 @@ export function convertPathData(pathData, {
|
|
|
170
178
|
|
|
171
179
|
export function optimizeArcPathData(pathData = []) {
|
|
172
180
|
|
|
173
|
-
|
|
181
|
+
//return pathData
|
|
174
182
|
|
|
175
|
-
|
|
183
|
+
let remove = []
|
|
184
|
+
let l = pathData.length;
|
|
185
|
+
let pathDataN = [];
|
|
186
|
+
|
|
187
|
+
for (let i = 0; i < l; i++) {
|
|
188
|
+
let com = pathData[i];
|
|
176
189
|
let { type, values } = com;
|
|
177
|
-
if (type === 'A') {
|
|
178
|
-
let [rx, ry, largeArc, x, y] = [values[0], values[1], values[3], values[5], values[6]];
|
|
179
|
-
let comPrev = pathData[i - 1]
|
|
180
|
-
let [x0, y0] = [comPrev.values[comPrev.values.length - 2], comPrev.values[comPrev.values.length - 1]];
|
|
181
|
-
let M = { x: x0, y: y0 };
|
|
182
|
-
let p = { x, y };
|
|
183
|
-
//largeArc=true
|
|
184
|
-
//let pMid = {x: Math.abs(x-x0), y:Math.abs(y-y0)}
|
|
185
|
-
|
|
186
|
-
if(rx===0 || ry===0){
|
|
187
|
-
pathData[i]= null
|
|
188
|
-
remove.push(i)
|
|
189
|
-
//console.log('!!!');
|
|
190
|
-
}
|
|
191
190
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
191
|
+
if (type !== 'A') {
|
|
192
|
+
pathDataN.push(com)
|
|
193
|
+
continue
|
|
194
|
+
}
|
|
195
195
|
|
|
196
|
-
// rx~==ry
|
|
197
|
-
if (diff < 0.01) {
|
|
198
196
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
197
|
+
let [rx, ry, largeArc, x, y] = [values[0], values[1], values[3], values[5], values[6]];
|
|
198
|
+
let comPrev = pathData[i - 1]
|
|
199
|
+
let [x0, y0] = [comPrev.values[comPrev.values.length - 2], comPrev.values[comPrev.values.length - 1]];
|
|
200
|
+
let M = { x: x0, y: y0 };
|
|
201
|
+
let p = { x, y };
|
|
202
|
+
//largeArc=true
|
|
203
|
+
//let pMid = {x: Math.abs(x-x0), y:Math.abs(y-y0)}
|
|
203
204
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
205
|
+
if (rx === 0 || ry === 0) {
|
|
206
|
+
pathData[i] = null
|
|
207
|
+
remove.push(i)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// test for elliptic
|
|
211
|
+
let rat = rx / ry
|
|
212
|
+
let error = rx !== ry ? Math.abs(1 - rat) : 0
|
|
213
|
+
|
|
214
|
+
if (error > 0.01) {
|
|
215
|
+
//console.log('is elliptic');
|
|
216
|
+
pathDataN.push(com)
|
|
217
|
+
continue
|
|
218
|
+
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// xAxis rotation is futile for circular arcs - reset
|
|
222
|
+
com.values[2] = 0
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* test semi circles
|
|
226
|
+
* rx and ry are large enough
|
|
227
|
+
*/
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
// 1. horizontal or vertical
|
|
231
|
+
let thresh = getDistManhattan(M, p) * 0.001;
|
|
232
|
+
let diffX = Math.abs(x - x0)
|
|
233
|
+
let diffY = Math.abs(y - y0)
|
|
234
|
+
|
|
235
|
+
let isHorizontal = diffY < thresh
|
|
236
|
+
let isVertical = diffX < thresh
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
// minify rx and ry
|
|
240
|
+
if (isHorizontal || isVertical) {
|
|
241
|
+
|
|
242
|
+
// check if semi circle
|
|
243
|
+
let needsTrueR = isHorizontal ? rx*1.9 > diffX : ry*1.9 > diffY;
|
|
244
|
+
|
|
245
|
+
// is semicircle we can simplify rx
|
|
246
|
+
if (!needsTrueR) {
|
|
247
|
+
//console.log('needsTrueR', needsTrueR, diffX, rx, diffY, ry);
|
|
248
|
+
rx = rx >= 1 ? 1 : (rx > 0.5 ? 0.5 : rx);
|
|
211
249
|
}
|
|
250
|
+
|
|
251
|
+
com.values[0] = rx
|
|
252
|
+
com.values[1] = rx
|
|
253
|
+
pathDataN.push(com)
|
|
254
|
+
continue
|
|
255
|
+
|
|
256
|
+
|
|
212
257
|
}
|
|
213
|
-
})
|
|
214
258
|
|
|
215
|
-
|
|
216
|
-
|
|
259
|
+
// 2. get true radius - if rx ~= diameter/distance we have a semicircle
|
|
260
|
+
let r = getDistance(M, p) * 0.5
|
|
261
|
+
error = rx / r
|
|
262
|
+
//console.log('rx', rx, r, error);
|
|
263
|
+
|
|
264
|
+
if (error < 0.5) {
|
|
265
|
+
rx = r >= 1 ? 1 : (r > 0.5 ? 0.5 : r);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
com.values[0] = rx;
|
|
269
|
+
com.values[1] = rx;
|
|
270
|
+
pathDataN.push(com)
|
|
271
|
+
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return pathDataN;
|
|
217
275
|
}
|
|
218
276
|
|
|
219
277
|
|
|
@@ -281,6 +339,48 @@ export function normalizePathData(pathData = [],
|
|
|
281
339
|
}
|
|
282
340
|
*/
|
|
283
341
|
|
|
342
|
+
export function convertSmallArcsToLinetos(pathData) {
|
|
343
|
+
|
|
344
|
+
let l = pathData.length;
|
|
345
|
+
|
|
346
|
+
// add fist command
|
|
347
|
+
let pathDataN = [pathData[0]]
|
|
348
|
+
|
|
349
|
+
for (let i = 1; i < l; i++) {
|
|
350
|
+
let com = pathData[i];
|
|
351
|
+
let comPrev = pathData[i - 1];
|
|
352
|
+
let comN = pathData[i + 1] || null;
|
|
353
|
+
|
|
354
|
+
if (!comN) {
|
|
355
|
+
pathDataN.push(com);
|
|
356
|
+
break
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
let { type, values, extreme = false, p0, p, dimA = 0 } = com;
|
|
360
|
+
// for short segment detection
|
|
361
|
+
let dimAN = comN.dimA;
|
|
362
|
+
let dimA0 = comPrev.dimA + dimA + dimAN;
|
|
363
|
+
let thresh = 0.05
|
|
364
|
+
let isShort = dimA < dimA0 * thresh;
|
|
365
|
+
//let isShortN = dimAN < dimA0 * thresh;
|
|
366
|
+
|
|
367
|
+
if (type === 'A' && isShort && values[0] < 1 && values[1] < 1) {
|
|
368
|
+
|
|
369
|
+
//renderPoint(markers, p0, 'green', '0.1%')
|
|
370
|
+
//renderPoint(markers, p, 'magenta', '0.1%')
|
|
371
|
+
com.type = 'L';
|
|
372
|
+
com.values = [p.x, p.y];
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
pathDataN.push(com)
|
|
376
|
+
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
return pathDataN;
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
}
|
|
284
384
|
|
|
285
385
|
|
|
286
386
|
|
|
@@ -54,33 +54,25 @@ export function fixPathDataDirections(pathDataArr = [], toClockwise = false) {
|
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
// reverse paths
|
|
57
|
-
for (let i = 0; i < l; i++) {
|
|
57
|
+
for (let i = 0; l && i < l; i++) {
|
|
58
58
|
|
|
59
59
|
let poly = polys[i]
|
|
60
60
|
let { cw, includedIn, includes } = poly
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|| !includedIn.length && !cw && toClockwise
|
|
65
|
-
) {
|
|
66
|
-
//console.log('reverse outer');
|
|
67
|
-
|
|
68
|
-
pathDataArr[i].pathData = reversePathData(pathDataArr[i].pathData);
|
|
69
|
-
polys[i].cw = polys[i].cw ? false : true
|
|
70
|
-
cw = polys[i].cw
|
|
71
|
-
|
|
72
|
-
}
|
|
62
|
+
let len = includes.length;
|
|
63
|
+
//console.log('try reverse', includes);
|
|
73
64
|
|
|
74
65
|
// reverse inner sub paths
|
|
75
|
-
for (let j = 0; j <
|
|
66
|
+
for (let j = 0; len && j < len; j++) {
|
|
76
67
|
let ind = includes[j];
|
|
77
68
|
let child = polys[ind];
|
|
78
69
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
70
|
+
// nothing to do
|
|
71
|
+
if (child.cw !== cw) continue
|
|
72
|
+
|
|
73
|
+
pathDataArr[ind].pathData = reversePathData(pathDataArr[ind].pathData);
|
|
74
|
+
polys[ind].cw = polys[ind].cw ? false : true
|
|
75
|
+
|
|
84
76
|
}
|
|
85
77
|
}
|
|
86
78
|
|
|
@@ -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,8 @@ export function pathDataToTopLeft(pathData) {
|
|
|
30
29
|
let { type, values } = com;
|
|
31
30
|
let valsLen = values.length
|
|
32
31
|
if (valsLen) {
|
|
33
|
-
|
|
32
|
+
// we need rounding otherwise sorting may crash due to e notation
|
|
33
|
+
let p = { type: type, x: +values[valsLen - 2].toFixed(8), y: +values[valsLen - 1].toFixed(8), index: 0 }
|
|
34
34
|
p.index = i
|
|
35
35
|
indices.push(p)
|
|
36
36
|
}
|
|
@@ -38,16 +38,124 @@ export function pathDataToTopLeft(pathData) {
|
|
|
38
38
|
|
|
39
39
|
// reorder to top left most
|
|
40
40
|
//|| a.x - b.x
|
|
41
|
-
indices = indices.sort((a, b) =>
|
|
41
|
+
indices = indices.sort((a, b) => a.y - b.y || a.x - b.x);
|
|
42
42
|
newIndex = indices[0].index
|
|
43
43
|
|
|
44
|
-
return
|
|
44
|
+
return newIndex ? shiftSvgStartingPoint(pathData, newIndex) : pathData;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
export function optimizeClosePath(pathData, { removeFinalLineto = true, autoClose = true } = {}) {
|
|
48
49
|
|
|
50
|
+
let pathDataN = pathData;
|
|
51
|
+
let l = pathData.length;
|
|
52
|
+
let M = { x: +pathData[0].values[0].toFixed(8), y: +pathData[0].values[1].toFixed(8) }
|
|
53
|
+
let isClosed = pathData[l - 1].type.toLowerCase() === 'z'
|
|
54
|
+
//let linetos = pathData.filter(com => com.type === 'L')
|
|
55
|
+
//let hasLinetos = linetos.length;
|
|
56
|
+
let hasLinetos = false
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
// check if path is closed by explicit lineto
|
|
60
|
+
let idxPenultimate = isClosed ? l - 2 : l - 1
|
|
61
|
+
let penultimateCom = pathData[idxPenultimate];
|
|
62
|
+
let penultimateType = penultimateCom.type;
|
|
63
|
+
let penultimateComCoords = penultimateCom.values.slice(-2).map(val => +val.toFixed(8))
|
|
64
|
+
|
|
65
|
+
// last L command ends at M
|
|
66
|
+
let hasClosingCommand = penultimateComCoords[0] === M.x && penultimateComCoords[1] === M.y
|
|
67
|
+
let lastIsLine = penultimateType === 'L'
|
|
68
|
+
//console.log(pathData);
|
|
69
|
+
|
|
70
|
+
// create index
|
|
71
|
+
let indices = [];
|
|
72
|
+
for (let i = 0; i < l; i++) {
|
|
73
|
+
let com = pathData[i];
|
|
74
|
+
let { type, values, p0, p } = com;
|
|
75
|
+
|
|
76
|
+
if(type==='L') hasLinetos = true;
|
|
77
|
+
|
|
78
|
+
// exclude Z
|
|
79
|
+
if (values.length) {
|
|
80
|
+
let valsL = values.slice(-2)
|
|
81
|
+
|
|
82
|
+
let x = Math.min(p0.x, p.x)
|
|
83
|
+
let y = Math.min(p0.y, p.y)
|
|
84
|
+
|
|
85
|
+
let prevCom = pathData[i - 1] ? pathData[i - 1] : pathData[idxPenultimate]
|
|
86
|
+
let prevType = prevCom.type
|
|
87
|
+
//let p = { type: type, x: valsL[0], y: valsL[1], dist: 0, index: 0, prevType }
|
|
88
|
+
let item = { type: type, x, y, index: 0, prevType }
|
|
89
|
+
item.index = i
|
|
90
|
+
indices.push(item)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let xMin = Infinity;
|
|
96
|
+
let yMin = Infinity;
|
|
97
|
+
let idx_top = null;
|
|
98
|
+
let len = indices.length
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
for (let i = 0; i < len; i++) {
|
|
102
|
+
let com = indices[i];
|
|
103
|
+
let { type, index, x, y, prevType } = com;
|
|
104
|
+
|
|
105
|
+
if (hasLinetos && prevType === 'L') {
|
|
106
|
+
if (x < xMin && y < yMin) {
|
|
107
|
+
idx_top = index-1;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (y < yMin) {
|
|
111
|
+
yMin = y
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (x < xMin) {
|
|
115
|
+
xMin = x
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
// shift to better starting point
|
|
122
|
+
if (idx_top) {
|
|
123
|
+
pathDataN = shiftSvgStartingPoint(pathDataN, idx_top)
|
|
124
|
+
|
|
125
|
+
// update penultimate - reorder might have added new close paths
|
|
126
|
+
l = pathDataN.length
|
|
127
|
+
M = { x: +pathDataN[0].values[0].toFixed(8), y: +pathDataN[0].values[1].toFixed(8) }
|
|
128
|
+
|
|
129
|
+
idxPenultimate = isClosed ? l - 2 : l - 1
|
|
130
|
+
penultimateCom = pathDataN[idxPenultimate];
|
|
131
|
+
penultimateType = penultimateCom.type;
|
|
132
|
+
penultimateComCoords = penultimateCom.values.slice(-2).map(val => +val.toFixed(8))
|
|
133
|
+
lastIsLine = penultimateType ==='L'
|
|
134
|
+
|
|
135
|
+
// last L command ends at M
|
|
136
|
+
hasClosingCommand = penultimateComCoords[0] === M.x && penultimateComCoords[1] === M.y
|
|
137
|
+
|
|
138
|
+
}
|
|
49
139
|
|
|
50
|
-
|
|
140
|
+
|
|
141
|
+
// remove unnecessary closing lineto
|
|
142
|
+
if (removeFinalLineto && hasClosingCommand && lastIsLine) {
|
|
143
|
+
pathDataN.splice(l - 2, 1)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// add close path
|
|
147
|
+
if (autoClose && !isClosed && hasClosingCommand) {
|
|
148
|
+
pathDataN.push({ type: 'Z', values: [] })
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return pathDataN
|
|
152
|
+
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
export function optimizeClosePath__(pathData, { removeFinalLineto = true, autoClose = true } = {}) {
|
|
51
159
|
|
|
52
160
|
let pathDataNew = [];
|
|
53
161
|
let l = pathData.length;
|
|
@@ -58,17 +166,16 @@ export function optimizeClosePath(pathData, {removeFinalLineto = true, autoClose
|
|
|
58
166
|
|
|
59
167
|
|
|
60
168
|
// check if order is ideal
|
|
61
|
-
let idxPenultimate = isClosed ? l-2 : l-1
|
|
62
|
-
|
|
169
|
+
let idxPenultimate = isClosed ? l - 2 : l - 1
|
|
63
170
|
let penultimateCom = pathData[idxPenultimate];
|
|
64
171
|
let penultimateType = penultimateCom.type;
|
|
65
172
|
let penultimateComCoords = penultimateCom.values.slice(-2).map(val => +val.toFixed(8))
|
|
66
173
|
|
|
67
174
|
// last L command ends at M
|
|
68
|
-
let
|
|
175
|
+
let hasClosingCommand = penultimateComCoords[0] === M.x && penultimateComCoords[1] === M.y
|
|
69
176
|
|
|
70
177
|
// add closepath Z to enable order optimizations
|
|
71
|
-
if(!isClosed && autoClose &&
|
|
178
|
+
if (!isClosed && autoClose && hasClosingCommand) {
|
|
72
179
|
|
|
73
180
|
/*
|
|
74
181
|
// adjust final coords
|
|
@@ -77,14 +184,14 @@ export function optimizeClosePath(pathData, {removeFinalLineto = true, autoClose
|
|
|
77
184
|
pathData[idxPenultimate].values[valsLastLen-2] = M.x
|
|
78
185
|
pathData[idxPenultimate].values[valsLastLen-1] = M.y
|
|
79
186
|
*/
|
|
80
|
-
|
|
81
|
-
pathData.push({type:'Z', values:[]})
|
|
187
|
+
|
|
188
|
+
pathData.push({ type: 'Z', values: [] })
|
|
82
189
|
isClosed = true;
|
|
83
190
|
l++
|
|
84
191
|
}
|
|
85
192
|
|
|
86
193
|
// if last segment is not closing or a lineto
|
|
87
|
-
let skipReorder = pathData[1].type !== 'L' && (!
|
|
194
|
+
let skipReorder = pathData[1].type !== 'L' && (!hasClosingCommand || penultimateCom.type === 'L')
|
|
88
195
|
skipReorder = false
|
|
89
196
|
|
|
90
197
|
|
|
@@ -143,13 +250,13 @@ export function optimizeClosePath(pathData, {removeFinalLineto = true, autoClose
|
|
|
143
250
|
// remove last lineto
|
|
144
251
|
penultimateCom = pathData[l - 2];
|
|
145
252
|
penultimateType = penultimateCom.type;
|
|
146
|
-
penultimateComCoords = penultimateCom.values.slice(-2).map(val
|
|
253
|
+
penultimateComCoords = penultimateCom.values.slice(-2).map(val => +val.toFixed(8))
|
|
147
254
|
|
|
148
|
-
|
|
255
|
+
hasClosingCommand = penultimateType === 'L' && penultimateComCoords[0] === M.x && penultimateComCoords[1] === M.y
|
|
149
256
|
|
|
150
|
-
//console.log('penultimateCom',
|
|
257
|
+
//console.log('penultimateCom', hasClosingCommand, penultimateCom.values, M);
|
|
151
258
|
|
|
152
|
-
if (removeFinalLineto &&
|
|
259
|
+
if (removeFinalLineto && hasClosingCommand) {
|
|
153
260
|
pathData.splice(l - 2, 1)
|
|
154
261
|
}
|
|
155
262
|
|