@@ -243,15 +243,21 @@ const parsePathData = (string) => {
243
243
exports . parsePathData = parsePathData ;
244
244
245
245
/**
246
- * @type {(number: number, precision?: number) => string }
246
+ * @type {(number: number, precision?: number) => {
247
+ * roundedStr: string,
248
+ * rounded: number
249
+ * }}
247
250
*/
248
- const stringifyNumber = ( number , precision ) => {
251
+ const roundAndStringify = ( number , precision ) => {
249
252
if ( precision != null ) {
250
253
const ratio = 10 ** precision ;
251
254
number = Math . round ( number * ratio ) / ratio ;
252
255
}
253
- // remove zero whole from decimal number
254
- return removeLeadingZero ( number ) ;
256
+
257
+ return {
258
+ roundedStr : removeLeadingZero ( number ) ,
259
+ rounded : number ,
260
+ } ;
255
261
} ;
256
262
257
263
/**
@@ -267,29 +273,35 @@ const stringifyNumber = (number, precision) => {
267
273
*/
268
274
const stringifyArgs = ( command , args , precision , disableSpaceAfterFlags ) => {
269
275
let result = '' ;
270
- let prev = '' ;
271
- for ( let i = 0 ; i < args . length ; i += 1 ) {
272
- const number = args [ i ] ;
273
- const numberString = stringifyNumber ( number , precision ) ;
276
+ let previous ;
277
+
278
+ for ( let i = 0 ; i < args . length ; i ++ ) {
279
+ const { roundedStr , rounded } = roundAndStringify ( args [ i ] , precision ) ;
274
280
if (
275
281
disableSpaceAfterFlags &&
276
282
( command === 'A' || command === 'a' ) &&
277
283
// consider combined arcs
278
284
( i % 7 === 4 || i % 7 === 5 )
279
285
) {
280
- result += numberString ;
281
- } else if ( i === 0 || numberString . startsWith ( '-' ) ) {
286
+ result += roundedStr ;
287
+ } else if ( i === 0 || rounded < 0 ) {
282
288
// avoid space before first and negative numbers
283
- result += numberString ;
284
- } else if ( prev . includes ( '.' ) && numberString . startsWith ( '.' ) ) {
289
+ result += roundedStr ;
290
+ } else if (
291
+ ! Number . isInteger ( previous ) &&
292
+ rounded != 0 &&
293
+ rounded < 1 &&
294
+ rounded > - 1
295
+ ) {
285
296
// remove space before decimal with zero whole
286
297
// only when previous number is also decimal
287
- result += numberString ;
298
+ result += roundedStr ;
288
299
} else {
289
- result += ` ${ numberString } ` ;
300
+ result += ` ${ roundedStr } ` ;
290
301
}
291
- prev = numberString ;
302
+ previous = rounded ;
292
303
}
304
+
293
305
return result ;
294
306
} ;
295
307
@@ -302,48 +314,68 @@ const stringifyArgs = (command, args, precision, disableSpaceAfterFlags) => {
302
314
*/
303
315
304
316
/**
305
- * @type {(options: StringifyPathDataOptions) => string }
317
+ * @param {StringifyPathDataOptions } options
318
+ * @returns {string }
306
319
*/
307
320
const stringifyPathData = ( { pathData, precision, disableSpaceAfterFlags } ) => {
308
- // combine sequence of the same commands
309
- let combined = [ ] ;
310
- for ( let i = 0 ; i < pathData . length ; i += 1 ) {
321
+ if ( pathData . length === 1 ) {
322
+ const { command, args } = pathData [ 0 ] ;
323
+ return (
324
+ command + stringifyArgs ( command , args , precision , disableSpaceAfterFlags )
325
+ ) ;
326
+ }
327
+
328
+ let result = '' ;
329
+ let prev = { ...pathData [ 0 ] } ;
330
+
331
+ // match leading moveto with following lineto
332
+ if ( pathData [ 1 ] . command === 'L' ) {
333
+ prev . command = 'M' ;
334
+ } else if ( pathData [ 1 ] . command === 'l' ) {
335
+ prev . command = 'm' ;
336
+ }
337
+
338
+ for ( let i = 1 ; i < pathData . length ; i ++ ) {
311
339
const { command, args } = pathData [ i ] ;
312
- if ( i === 0 ) {
313
- combined . push ( { command, args } ) ;
314
- } else {
315
- /**
316
- * @type {PathDataItem }
317
- */
318
- const last = combined [ combined . length - 1 ] ;
319
- // match leading moveto with following lineto
320
- if ( i === 1 ) {
321
- if ( command === 'L' ) {
322
- last . command = 'M' ;
323
- }
324
- if ( command === 'l' ) {
325
- last . command = 'm' ;
326
- }
340
+ if (
341
+ ( prev . command === command &&
342
+ prev . command !== 'M' &&
343
+ prev . command !== 'm' ) ||
344
+ // combine matching moveto and lineto sequences
345
+ ( prev . command === 'M' && command === 'L' ) ||
346
+ ( prev . command === 'm' && command === 'l' )
347
+ ) {
348
+ prev . args = [ ...prev . args , ...args ] ;
349
+ if ( i === pathData . length - 1 ) {
350
+ result +=
351
+ prev . command +
352
+ stringifyArgs (
353
+ prev . command ,
354
+ prev . args ,
355
+ precision ,
356
+ disableSpaceAfterFlags ,
357
+ ) ;
327
358
}
328
- if (
329
- ( last . command === command &&
330
- last . command !== 'M' &&
331
- last . command !== 'm' ) ||
332
- // combine matching moveto and lineto sequences
333
- ( last . command === 'M' && command === 'L' ) ||
334
- ( last . command === 'm' && command === 'l' )
335
- ) {
336
- last . args = [ ...last . args , ...args ] ;
359
+ } else {
360
+ result +=
361
+ prev . command +
362
+ stringifyArgs (
363
+ prev . command ,
364
+ prev . args ,
365
+ precision ,
366
+ disableSpaceAfterFlags ,
367
+ ) ;
368
+
369
+ if ( i === pathData . length - 1 ) {
370
+ result +=
371
+ command +
372
+ stringifyArgs ( command , args , precision , disableSpaceAfterFlags ) ;
337
373
} else {
338
- combined . push ( { command, args } ) ;
374
+ prev = { command, args } ;
339
375
}
340
376
}
341
377
}
342
- let result = '' ;
343
- for ( const { command, args } of combined ) {
344
- result +=
345
- command + stringifyArgs ( command , args , precision , disableSpaceAfterFlags ) ;
346
- }
378
+
347
379
return result ;
348
380
} ;
349
381
exports . stringifyPathData = stringifyPathData ;
0 commit comments