svg-path-simplify 0.1.3 → 0.2.2
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 +10 -0
- package/dist/svg-path-simplify.esm.js +3905 -1533
- package/dist/svg-path-simplify.esm.min.js +13 -1
- package/dist/svg-path-simplify.js +3923 -1551
- package/dist/svg-path-simplify.min.js +13 -1
- package/dist/svg-path-simplify.min.js.gz +0 -0
- package/index.html +61 -31
- package/package.json +3 -5
- package/src/constants.js +3 -0
- package/src/index-node.js +0 -1
- package/src/index.js +26 -0
- package/src/pathData_simplify_cubic.js +74 -31
- package/src/pathData_simplify_cubicsToArcs.js +566 -0
- package/src/pathData_simplify_harmonize_cpts.js +170 -0
- package/src/pathData_simplify_revertToquadratics.js +21 -0
- package/src/pathSimplify-main.js +253 -86
- package/src/poly-fit-curve-schneider.js +570 -0
- package/src/simplify_poly_RDP.js +146 -0
- package/src/simplify_poly_radial_distance.js +100 -0
- package/src/svg_getViewbox.js +1 -1
- package/src/svgii/geometry.js +389 -63
- package/src/svgii/geometry_area.js +2 -1
- package/src/svgii/pathData_analyze.js +259 -212
- package/src/svgii/pathData_convert.js +91 -663
- package/src/svgii/pathData_fromPoly.js +12 -0
- package/src/svgii/pathData_parse.js +90 -89
- package/src/svgii/pathData_parse_els.js +3 -0
- package/src/svgii/pathData_parse_fontello.js +449 -0
- package/src/svgii/pathData_remove_collinear.js +44 -37
- package/src/svgii/pathData_reorder.js +2 -1
- package/src/svgii/pathData_simplify_redraw.js +343 -0
- package/src/svgii/pathData_simplify_refineCorners.js +18 -9
- package/src/svgii/pathData_simplify_refineExtremes.js +19 -78
- package/src/svgii/pathData_split.js +42 -45
- package/src/svgii/pathData_toPolygon.js +130 -4
- package/src/svgii/poly_analyze.js +470 -14
- package/src/svgii/poly_to_pathdata.js +224 -19
- package/src/svgii/rounding.js +55 -112
- package/src/svgii/svg_cleanup.js +13 -1
- package/src/svgii/visualize.js +8 -3
- package/{debug.cjs → tests/debug.cjs} +3 -0
- /package/{test.js → tests/test.js} +0 -0
- /package/{testSVG.js → tests/testSVG.js} +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function pathDataFromPoly(pts, closed=true){
|
|
2
|
+
|
|
3
|
+
let pathData = [
|
|
4
|
+
{ type: 'M', values: [pts[0].x, pts[0].y] },
|
|
5
|
+
...pts.slice(1).map(pt => { return { type: 'L', values: [pt.x, pt.y] } })
|
|
6
|
+
];
|
|
7
|
+
|
|
8
|
+
if(closed) pathData.push({type:'Z', values:[]})
|
|
9
|
+
|
|
10
|
+
return pathData
|
|
11
|
+
|
|
12
|
+
}
|
|
@@ -150,38 +150,77 @@ paramCountsArr[0x5A] = 0
|
|
|
150
150
|
paramCountsArr[0x7A] = 0
|
|
151
151
|
|
|
152
152
|
|
|
153
|
+
const SPECIAL_SPACES = new Set([
|
|
154
|
+
0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006,
|
|
155
|
+
0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF
|
|
156
|
+
]);
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
const isSpace = (ch) => {
|
|
160
|
+
return (ch === 0x20) || (ch === 0x002C) || // White spaces or comma
|
|
161
|
+
(ch === 0x0A) || (ch === 0x0D) || // nl cr
|
|
162
|
+
(ch === 0x2028) || (ch === 0x2029) || // Line terminators
|
|
163
|
+
(ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) ||
|
|
164
|
+
(ch >= 0x1680 && SPECIAL_SPACES.has(ch));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
const sanitizeArc = (val='', valueIndex=0) => {
|
|
169
|
+
let valLen = val.length;
|
|
170
|
+
|
|
171
|
+
// large arc and sweep
|
|
172
|
+
if (valueIndex === 3 && valLen === 2) {
|
|
173
|
+
//console.log('large arc sweep combined', val, +val[0], +val[1]);
|
|
174
|
+
val = [+val[0], +val[1]];
|
|
175
|
+
valueIndex++
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// sweep and final
|
|
179
|
+
else if (valueIndex === 4 && valLen > 1) {
|
|
180
|
+
//console.log('sweep and final', val, val[0], val[1]);
|
|
181
|
+
val = [+val[0], +val.substring(1)];
|
|
182
|
+
valueIndex++
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// large arc, sweep and final pt combined
|
|
186
|
+
else if (valueIndex === 3 && valLen >= 3) {
|
|
187
|
+
//console.log('large arc, sweep and final pt combined', val);
|
|
188
|
+
val = [+val[0], +val[1], +val.substring(2)];
|
|
189
|
+
valueIndex += 2
|
|
190
|
+
}
|
|
191
|
+
else{
|
|
192
|
+
val = [+val]
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
//console.log('val arc', val);
|
|
196
|
+
return {val,valueIndex} ;
|
|
197
|
+
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
|
|
153
201
|
|
|
154
202
|
export function parsePathDataString(d, debug = true) {
|
|
155
203
|
d = d.trim();
|
|
156
204
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
205
|
+
let pathDataObj = {
|
|
206
|
+
pathData: [],
|
|
207
|
+
hasRelatives: false,
|
|
208
|
+
hasShorthands: false,
|
|
209
|
+
hasArcs: false,
|
|
210
|
+
hasQuadratics: false,
|
|
211
|
+
isPolygon: false,
|
|
212
|
+
log:[]
|
|
165
213
|
}
|
|
166
214
|
|
|
167
|
-
const SPECIAL_SPACES = new Set([
|
|
168
|
-
0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006,
|
|
169
|
-
0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF
|
|
170
|
-
]);
|
|
171
|
-
|
|
172
215
|
|
|
173
|
-
|
|
174
|
-
return
|
|
175
|
-
(ch === 0x0A) || (ch === 0x0D) || // nl cr
|
|
176
|
-
(ch === 0x2028) || (ch === 0x2029) || // Line terminators
|
|
177
|
-
(ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) ||
|
|
178
|
-
(ch >= 0x1680 && SPECIAL_SPACES.has(ch));
|
|
216
|
+
if (d === '') {
|
|
217
|
+
return pathDataObj
|
|
179
218
|
}
|
|
180
219
|
|
|
181
220
|
|
|
182
221
|
let i = 0, len = d.length;
|
|
183
222
|
let lastCommand = "";
|
|
184
|
-
let pathData = [];
|
|
223
|
+
//let pathData = [];
|
|
185
224
|
let itemCount = -1;
|
|
186
225
|
let val = '';
|
|
187
226
|
let wasE = false;
|
|
@@ -193,7 +232,7 @@ export function parsePathDataString(d, debug = true) {
|
|
|
193
232
|
|
|
194
233
|
|
|
195
234
|
// collect errors
|
|
196
|
-
let log = [];
|
|
235
|
+
//let log = [];
|
|
197
236
|
let feedback;
|
|
198
237
|
|
|
199
238
|
const addSeg = () => {
|
|
@@ -204,7 +243,7 @@ export function parsePathDataString(d, debug = true) {
|
|
|
204
243
|
if (lastCommand === 'M') lastCommand = 'L';
|
|
205
244
|
else if (lastCommand === 'm') lastCommand = 'l';
|
|
206
245
|
|
|
207
|
-
pathData.push({ type: lastCommand, values: [] });
|
|
246
|
+
pathDataObj.pathData.push({ type: lastCommand, values: [] });
|
|
208
247
|
|
|
209
248
|
itemCount++;
|
|
210
249
|
valueIndex = 0;
|
|
@@ -221,11 +260,11 @@ export function parsePathDataString(d, debug = true) {
|
|
|
221
260
|
if (debug && itemCount === -1) {
|
|
222
261
|
|
|
223
262
|
feedback = 'Pathdata must start with M command'
|
|
224
|
-
log.push(feedback)
|
|
263
|
+
pathDataObj.log.push(feedback)
|
|
225
264
|
|
|
226
265
|
// add M command to collect subsequent errors
|
|
227
266
|
lastCommand = 'M'
|
|
228
|
-
pathData.push({ type: lastCommand, values: [] });
|
|
267
|
+
pathDataObj.pathData.push({ type: lastCommand, values: [] });
|
|
229
268
|
maxParams = 2;
|
|
230
269
|
valueIndex = 0
|
|
231
270
|
itemCount++
|
|
@@ -233,17 +272,17 @@ export function parsePathDataString(d, debug = true) {
|
|
|
233
272
|
}
|
|
234
273
|
|
|
235
274
|
if (lastCommand === 'A' || lastCommand === 'a') {
|
|
236
|
-
val
|
|
275
|
+
({val,valueIndex} = sanitizeArc(val, valueIndex));
|
|
237
276
|
//console.log('arc', val);
|
|
238
|
-
pathData[itemCount].values.push(...val);
|
|
277
|
+
pathDataObj.pathData[itemCount].values.push(...val);
|
|
239
278
|
|
|
240
279
|
} else {
|
|
241
280
|
// error: leading zeroes
|
|
242
281
|
if (debug && val[1] && val[1] !== '.' && val[0] === '0') {
|
|
243
282
|
feedback = `${itemCount}. command: Leading zeros not valid: ${val}`
|
|
244
|
-
log.push(feedback)
|
|
283
|
+
pathDataObj.log.push(feedback)
|
|
245
284
|
}
|
|
246
|
-
pathData[itemCount].values.push(+val);
|
|
285
|
+
pathDataObj.pathData[itemCount].values.push(+val);
|
|
247
286
|
}
|
|
248
287
|
|
|
249
288
|
valueIndex++;
|
|
@@ -256,54 +295,21 @@ export function parsePathDataString(d, debug = true) {
|
|
|
256
295
|
}
|
|
257
296
|
}
|
|
258
297
|
|
|
259
|
-
const sanitizeArc = () => {
|
|
260
|
-
|
|
261
|
-
let valLen = val.length;
|
|
262
|
-
let arcSucks = false;
|
|
263
|
-
|
|
264
|
-
// large arc and sweep
|
|
265
|
-
if (valueIndex === 3 && valLen === 2) {
|
|
266
|
-
//console.log('large arc sweep combined', val, +val[0], +val[1]);
|
|
267
|
-
val = [+val[0], +val[1]];
|
|
268
|
-
arcSucks = true
|
|
269
|
-
valueIndex++
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// sweep and final
|
|
273
|
-
else if (valueIndex === 4 && valLen > 1) {
|
|
274
|
-
//console.log('sweep and final', val, val[0], val[1]);
|
|
275
|
-
val = [+val[0], +val[1]];
|
|
276
|
-
arcSucks = true
|
|
277
|
-
valueIndex++
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// large arc, sweep and final pt combined
|
|
281
|
-
else if (valueIndex === 3 && valLen >= 3) {
|
|
282
|
-
//console.log('large arc, sweep and final pt combined', val);
|
|
283
|
-
val = [+val[0], +val[1], +val.substring(2)];
|
|
284
|
-
arcSucks = true
|
|
285
|
-
valueIndex += 2
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
//console.log('val arc', val);
|
|
289
|
-
return !arcSucks ? [+val] : val;
|
|
290
|
-
|
|
291
|
-
}
|
|
292
298
|
|
|
293
299
|
const validateCommand = () => {
|
|
294
300
|
|
|
295
301
|
if (itemCount > 0) {
|
|
296
|
-
let lastCom = pathData[itemCount];
|
|
302
|
+
let lastCom = pathDataObj.pathData[itemCount];
|
|
297
303
|
let valLen = lastCom.values.length;
|
|
298
304
|
|
|
299
305
|
if ((valLen && valLen < maxParams) || (valLen && valLen > maxParams) || ((lastCommand === 'z' || lastCommand === 'Z') && valLen > 0)) {
|
|
300
306
|
let diff = maxParams - valLen;
|
|
301
307
|
feedback = `${itemCount}. command of type "${lastCommand}": ${diff} values too few - ${maxParams} expected`;
|
|
302
308
|
|
|
303
|
-
let prevFeedback = log[log.length - 1];
|
|
309
|
+
let prevFeedback = pathDataObj.log[pathDataObj.log.length - 1];
|
|
304
310
|
|
|
305
311
|
if (prevFeedback !== feedback) {
|
|
306
|
-
log.push(feedback)
|
|
312
|
+
pathDataObj.log.push(feedback)
|
|
307
313
|
}
|
|
308
314
|
}
|
|
309
315
|
}
|
|
@@ -313,12 +319,11 @@ export function parsePathDataString(d, debug = true) {
|
|
|
313
319
|
let isE = false;
|
|
314
320
|
let isMinusorPlus = false;
|
|
315
321
|
let isDot = false;
|
|
316
|
-
|
|
322
|
+
let charCode='';
|
|
317
323
|
|
|
318
324
|
while (i < len) {
|
|
319
325
|
|
|
320
|
-
|
|
321
|
-
|
|
326
|
+
charCode = d.charCodeAt(i);
|
|
322
327
|
|
|
323
328
|
let isDigit = (charCode > 47 && charCode < 58);
|
|
324
329
|
if (!isDigit) {
|
|
@@ -399,7 +404,7 @@ export function parsePathDataString(d, debug = true) {
|
|
|
399
404
|
|
|
400
405
|
if (!isValid) {
|
|
401
406
|
feedback = `${itemCount}. command "${d[i]}" is not a valid type`;
|
|
402
|
-
log.push(feedback);
|
|
407
|
+
pathDataObj.log.push(feedback);
|
|
403
408
|
i++
|
|
404
409
|
continue
|
|
405
410
|
}
|
|
@@ -407,7 +412,7 @@ export function parsePathDataString(d, debug = true) {
|
|
|
407
412
|
|
|
408
413
|
// command is concatenated without whitespace
|
|
409
414
|
if (val !== '') {
|
|
410
|
-
pathData[itemCount].values.push(+val);
|
|
415
|
+
pathDataObj.pathData[itemCount].values.push(+val);
|
|
411
416
|
valueIndex++;
|
|
412
417
|
val = '';
|
|
413
418
|
}
|
|
@@ -419,18 +424,18 @@ export function parsePathDataString(d, debug = true) {
|
|
|
419
424
|
lastCommand = d[i];
|
|
420
425
|
maxParams = paramCountsArr[charCode];
|
|
421
426
|
let isM = lastCommand === 'M' || lastCommand === 'm'
|
|
422
|
-
let wasClosePath = itemCount > 0 && (pathData[itemCount].type === 'z' || pathData[itemCount].type === 'Z')
|
|
427
|
+
let wasClosePath = itemCount > 0 && (pathDataObj.pathData[itemCount].type === 'z' || pathDataObj.pathData[itemCount].type === 'Z')
|
|
423
428
|
|
|
424
429
|
foundCommands.add(lastCommand);
|
|
425
430
|
|
|
426
431
|
// add omitted M command after Z
|
|
427
432
|
if (wasClosePath && !isM) {
|
|
428
|
-
pathData.push({ type: 'm', values: [0, 0] });
|
|
433
|
+
pathDataObj.pathData.push({ type: 'm', values: [0, 0] });
|
|
429
434
|
itemCount++;
|
|
430
435
|
}
|
|
431
436
|
|
|
432
437
|
// init new command
|
|
433
|
-
pathData.push({ type: lastCommand, values: [] });
|
|
438
|
+
pathDataObj.pathData.push({ type: lastCommand, values: [] });
|
|
434
439
|
itemCount++;
|
|
435
440
|
|
|
436
441
|
// reset counters
|
|
@@ -446,7 +451,7 @@ export function parsePathDataString(d, debug = true) {
|
|
|
446
451
|
// exceptions - prevent infinite loop
|
|
447
452
|
if (!isDigit) {
|
|
448
453
|
feedback = `${itemCount}. ${d[i]} is not a valid separarator or token`;
|
|
449
|
-
log.push(feedback);
|
|
454
|
+
pathDataObj.log.push(feedback);
|
|
450
455
|
val = '';
|
|
451
456
|
}
|
|
452
457
|
|
|
@@ -460,8 +465,8 @@ export function parsePathDataString(d, debug = true) {
|
|
|
460
465
|
|
|
461
466
|
|
|
462
467
|
// return error log
|
|
463
|
-
if (debug && log.length) {
|
|
464
|
-
feedback = 'Invalid path data:\n' + log.join('\n')
|
|
468
|
+
if (debug && pathDataObj.log.length) {
|
|
469
|
+
feedback = 'Invalid path data:\n' + pathDataObj.log.join('\n')
|
|
465
470
|
if (debug === 'log') {
|
|
466
471
|
console.log(feedback);
|
|
467
472
|
} else {
|
|
@@ -470,7 +475,7 @@ export function parsePathDataString(d, debug = true) {
|
|
|
470
475
|
}
|
|
471
476
|
}
|
|
472
477
|
|
|
473
|
-
pathData[0].type = 'M'
|
|
478
|
+
pathDataObj.pathData[0].type = 'M'
|
|
474
479
|
|
|
475
480
|
/**
|
|
476
481
|
* check if absolute/relative or
|
|
@@ -479,19 +484,15 @@ export function parsePathDataString(d, debug = true) {
|
|
|
479
484
|
*/
|
|
480
485
|
//check types relative arcs or quadratics
|
|
481
486
|
let commands = Array.from(foundCommands).join('');
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
hasShorthands,
|
|
492
|
-
hasQuadratics,
|
|
493
|
-
hasArcs
|
|
494
|
-
}
|
|
487
|
+
|
|
488
|
+
pathDataObj.hasRelatives = /[lcqamtsvh]/g.test(commands);
|
|
489
|
+
pathDataObj.hasShorthands = /[vhst]/gi.test(commands);
|
|
490
|
+
pathDataObj.hasArcs = /[a]/gi.test(commands);
|
|
491
|
+
pathDataObj.hasQuadratics = /[qt]/gi.test(commands);
|
|
492
|
+
pathDataObj.isPolygon = /[cqats]/gi.test(commands) ? false : true;
|
|
493
|
+
|
|
494
|
+
return pathDataObj
|
|
495
|
+
|
|
495
496
|
|
|
496
497
|
}
|
|
497
498
|
|