modern-path2d 1.5.1 → 1.5.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/dist/index.cjs CHANGED
@@ -302,980 +302,833 @@ class BoundingBox {
302
302
  }
303
303
  }
304
304
 
305
- function svgAngle(ux, uy, vx, vy) {
306
- const dot = ux * vx + uy * vy;
307
- const len = Math.sqrt(ux * ux + uy * uy) * Math.sqrt(vx * vx + vy * vy);
308
- let ang = Math.acos(Math.max(-1, Math.min(1, dot / len)));
309
- if (ux * vy - uy * vx < 0)
310
- ang = -ang;
311
- return ang;
305
+ function catmullRom(t, p0, p1, p2, p3) {
306
+ const v0 = (p2 - p0) * 0.5;
307
+ const v1 = (p3 - p1) * 0.5;
308
+ const t2 = t * t;
309
+ const t3 = t * t2;
310
+ return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1;
312
311
  }
313
- function parseArcCommand(path, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, start, end) {
314
- if (rx === 0 || ry === 0) {
315
- path.lineTo(end.x, end.y);
316
- return;
312
+
313
+ const PI = Math.PI;
314
+ const PI_2 = PI * 2;
315
+ function toKebabCase(str) {
316
+ return str.replace(/[^a-z0-9]/gi, "-").replace(/\B([A-Z])/g, "-$1").toLowerCase();
317
+ }
318
+ function getIntersectionPoint(p1, p2, q1, q2) {
319
+ const r = p2.clone().sub(p1);
320
+ const s = q2.clone().sub(q1);
321
+ const q1p1 = q1.clone().sub(p1);
322
+ const crossRS = r.cross(s);
323
+ if (crossRS === 0) {
324
+ return new Vector2(
325
+ (p1.x + q1.x) / 2,
326
+ (p1.y + q1.y) / 2
327
+ );
317
328
  }
318
- xAxisRotation = xAxisRotation * Math.PI / 180;
319
- rx = Math.abs(rx);
320
- ry = Math.abs(ry);
321
- const dx2 = (start.x - end.x) / 2;
322
- const dy2 = (start.y - end.y) / 2;
323
- const x1p = Math.cos(xAxisRotation) * dx2 + Math.sin(xAxisRotation) * dy2;
324
- const y1p = -Math.sin(xAxisRotation) * dx2 + Math.cos(xAxisRotation) * dy2;
325
- let rxs = rx * rx;
326
- let rys = ry * ry;
327
- const x1ps = x1p * x1p;
328
- const y1ps = y1p * y1p;
329
- const cr = x1ps / rxs + y1ps / rys;
330
- if (cr > 1) {
331
- const s = Math.sqrt(cr);
332
- rx = s * rx;
333
- ry = s * ry;
334
- rxs = rx * rx;
335
- rys = ry * ry;
329
+ const t = q1p1.cross(s) / crossRS;
330
+ if (Math.abs(t) > 1) {
331
+ return new Vector2(
332
+ (p1.x + q1.x) / 2,
333
+ (p1.y + q1.y) / 2
334
+ );
336
335
  }
337
- const dq = rxs * y1ps + rys * x1ps;
338
- const pq = (rxs * rys - dq) / dq;
339
- let q = Math.sqrt(Math.max(0, pq));
340
- if (largeArcFlag === sweepFlag)
341
- q = -q;
342
- const cxp = q * rx * y1p / ry;
343
- const cyp = -q * ry * x1p / rx;
344
- const cx = Math.cos(xAxisRotation) * cxp - Math.sin(xAxisRotation) * cyp + (start.x + end.x) / 2;
345
- const cy = Math.sin(xAxisRotation) * cxp + Math.cos(xAxisRotation) * cyp + (start.y + end.y) / 2;
346
- const theta = svgAngle(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);
347
- const delta = svgAngle((x1p - cxp) / rx, (y1p - cyp) / ry, (-x1p - cxp) / rx, (-y1p - cyp) / ry) % (Math.PI * 2);
348
- path.ellipse(cx, cy, rx, ry, xAxisRotation, theta, theta + delta, sweepFlag === 0);
336
+ return new Vector2(
337
+ p1.x + t * r.x,
338
+ p1.y + t * r.y
339
+ );
349
340
  }
350
341
 
351
- const RE$3 = {
352
- WHITESPACE: /[ \t\r\n]/,
353
- DIGIT: /\d/,
354
- SIGN: /[-+]/,
355
- POINT: /\./,
356
- COMMA: /,/,
357
- EXP: /e/i,
358
- FLAGS: /[01]/
359
- };
360
- function parsePathDataArgs(input, flags, stride = 0) {
361
- const SEP = 0;
362
- const INT = 1;
363
- const FLOAT = 2;
364
- const EXP = 3;
365
- let state = SEP;
366
- let seenComma = true;
367
- let number = "";
368
- let exponent = "";
369
- const result = [];
370
- function throwSyntaxError(current2, i, partial) {
371
- const error = new SyntaxError(`Unexpected character "${current2}" at index ${i}.`);
372
- error.partial = partial;
373
- throw error;
374
- }
375
- function newNumber() {
376
- if (number !== "") {
377
- if (exponent === "")
378
- result.push(Number(number));
379
- else result.push(Number(number) * 10 ** Number(exponent));
342
+ const FUNCTIONS_RE = /([\w-]+)\((.+?)\)/g;
343
+ const ARGS_RE = /[^,]+/g;
344
+ const ARG_RE = /([-e.\d]+)(.*)/;
345
+ function parseCssFunctions(propertyValue, context = {}) {
346
+ const functions = [];
347
+ let match;
348
+ while ((match = FUNCTIONS_RE.exec(propertyValue)) !== null) {
349
+ const [, name, value] = match;
350
+ if (name) {
351
+ functions.push({
352
+ name,
353
+ args: parseCssArgs(name, value, context)
354
+ });
380
355
  }
381
- number = "";
382
- exponent = "";
383
356
  }
384
- let current;
385
- const length = input.length;
386
- for (let i = 0; i < length; i++) {
387
- current = input[i];
388
- if (Array.isArray(flags) && flags.includes(result.length % stride) && RE$3.FLAGS.test(current)) {
389
- state = INT;
390
- number = current;
391
- newNumber();
392
- continue;
393
- }
394
- if (state === SEP) {
395
- if (RE$3.WHITESPACE.test(current)) {
396
- continue;
397
- }
398
- if (RE$3.DIGIT.test(current) || RE$3.SIGN.test(current)) {
399
- state = INT;
400
- number = current;
401
- continue;
402
- }
403
- if (RE$3.POINT.test(current)) {
404
- state = FLOAT;
405
- number = current;
406
- continue;
407
- }
408
- if (RE$3.COMMA.test(current)) {
409
- if (seenComma) {
410
- throwSyntaxError(current, i, result);
411
- }
412
- seenComma = true;
413
- }
414
- }
415
- if (state === INT) {
416
- if (RE$3.DIGIT.test(current)) {
417
- number += current;
418
- continue;
419
- }
420
- if (RE$3.POINT.test(current)) {
421
- number += current;
422
- state = FLOAT;
423
- continue;
424
- }
425
- if (RE$3.EXP.test(current)) {
426
- state = EXP;
427
- continue;
428
- }
429
- if (RE$3.SIGN.test(current) && number.length === 1 && RE$3.SIGN.test(number[0])) {
430
- throwSyntaxError(current, i, result);
431
- }
432
- }
433
- if (state === FLOAT) {
434
- if (RE$3.DIGIT.test(current)) {
435
- number += current;
436
- continue;
437
- }
438
- if (RE$3.EXP.test(current)) {
439
- state = EXP;
440
- continue;
441
- }
442
- if (RE$3.POINT.test(current) && number[number.length - 1] === ".") {
443
- throwSyntaxError(current, i, result);
444
- }
445
- }
446
- if (state === EXP) {
447
- if (RE$3.DIGIT.test(current)) {
448
- exponent += current;
449
- continue;
450
- }
451
- if (RE$3.SIGN.test(current)) {
452
- if (exponent === "") {
453
- exponent += current;
454
- continue;
455
- }
456
- if (exponent.length === 1 && RE$3.SIGN.test(exponent)) {
457
- throwSyntaxError(current, i, result);
458
- }
357
+ return functions;
358
+ }
359
+ function parseCssArgs(name, value, context = {}) {
360
+ const values = [];
361
+ let match;
362
+ let i = 0;
363
+ while ((match = ARGS_RE.exec(value)) !== null) {
364
+ values.push(
365
+ parseCssArg(name, match[0], {
366
+ ...context,
367
+ index: i++
368
+ })
369
+ );
370
+ }
371
+ return values;
372
+ }
373
+ function parseCssArg(name, value, context = {}) {
374
+ const { width = 1, height = 1, index = 0 } = context;
375
+ const matched = value.match(ARG_RE);
376
+ const result = {
377
+ unit: matched?.[2] ?? null,
378
+ value,
379
+ intValue: Number(matched?.[1]),
380
+ normalizedIntValue: 0,
381
+ normalizedDefaultIntValue: 0
382
+ };
383
+ switch (name) {
384
+ case "scale":
385
+ case "scaleX":
386
+ case "scaleY":
387
+ case "scale3d":
388
+ result.normalizedDefaultIntValue = 1;
389
+ break;
390
+ }
391
+ switch (result.unit) {
392
+ case "%":
393
+ result.normalizedIntValue = result.intValue / 100;
394
+ break;
395
+ case "rad":
396
+ result.normalizedIntValue = result.intValue / PI_2;
397
+ break;
398
+ case "deg":
399
+ result.normalizedIntValue = result.intValue / 360;
400
+ break;
401
+ case "px":
402
+ switch (index) {
403
+ case 0:
404
+ result.normalizedIntValue = result.intValue / width;
405
+ break;
406
+ case 1:
407
+ result.normalizedIntValue = result.intValue / height;
408
+ break;
459
409
  }
460
- }
461
- if (RE$3.WHITESPACE.test(current)) {
462
- newNumber();
463
- state = SEP;
464
- seenComma = false;
465
- } else if (RE$3.COMMA.test(current)) {
466
- newNumber();
467
- state = SEP;
468
- seenComma = true;
469
- } else if (RE$3.SIGN.test(current)) {
470
- newNumber();
471
- state = INT;
472
- number = current;
473
- } else if (RE$3.POINT.test(current)) {
474
- newNumber();
475
- state = FLOAT;
476
- number = current;
477
- } else {
478
- throwSyntaxError(current, i, result);
479
- }
410
+ break;
411
+ case "turn":
412
+ case "em":
413
+ // div fontSize
414
+ case "rem":
415
+ // div fontSize
416
+ default:
417
+ result.normalizedIntValue = result.intValue;
418
+ break;
480
419
  }
481
- newNumber();
482
420
  return result;
483
421
  }
484
422
 
485
- function getReflection(a, b) {
486
- return a - (b - a);
423
+ function cubicBezierP0(t, p) {
424
+ const k = 1 - t;
425
+ return k * k * k * p;
487
426
  }
488
- function svgPathCommandsAddToPath2D(commands, path) {
489
- const current = new Vector2();
490
- const control = new Vector2();
491
- for (let i = 0, l = commands.length; i < l; i++) {
492
- const cmd = commands[i];
493
- if (cmd.type === "m" || cmd.type === "M") {
494
- if (cmd.type === "m") {
495
- current.add(cmd);
496
- } else {
497
- current.copyFrom(cmd);
498
- }
499
- path.moveTo(current.x, current.y);
500
- control.copyFrom(current);
501
- } else if (cmd.type === "h" || cmd.type === "H") {
502
- if (cmd.type === "h") {
503
- current.x += cmd.x;
504
- } else {
505
- current.x = cmd.x;
506
- }
507
- path.lineTo(current.x, current.y);
508
- control.copyFrom(current);
509
- } else if (cmd.type === "v" || cmd.type === "V") {
510
- if (cmd.type === "v") {
511
- current.y += cmd.y;
512
- } else {
513
- current.y = cmd.y;
514
- }
515
- path.lineTo(current.x, current.y);
516
- control.copyFrom(current);
517
- } else if (cmd.type === "l" || cmd.type === "L") {
518
- if (cmd.type === "l") {
519
- current.add(cmd);
520
- } else {
521
- current.copyFrom(cmd);
522
- }
523
- path.lineTo(current.x, current.y);
524
- control.copyFrom(current);
525
- } else if (cmd.type === "c" || cmd.type === "C") {
526
- if (cmd.type === "c") {
527
- path.bezierCurveTo(
528
- current.x + cmd.x1,
529
- current.y + cmd.y1,
530
- current.x + cmd.x2,
531
- current.y + cmd.y2,
532
- current.x + cmd.x,
533
- current.y + cmd.y
534
- );
535
- control.x = current.x + cmd.x2;
536
- control.y = current.y + cmd.y2;
537
- current.add(cmd);
538
- } else {
539
- path.bezierCurveTo(
540
- cmd.x1,
541
- cmd.y1,
542
- cmd.x2,
543
- cmd.y2,
544
- cmd.x,
545
- cmd.y
546
- );
547
- control.x = cmd.x2;
548
- control.y = cmd.y2;
549
- current.copyFrom(cmd);
550
- }
551
- } else if (cmd.type === "s" || cmd.type === "S") {
552
- if (cmd.type === "s") {
553
- path.bezierCurveTo(
554
- getReflection(current.x, control.x),
555
- getReflection(current.y, control.y),
556
- current.x + cmd.x2,
557
- current.y + cmd.y2,
558
- current.x + cmd.x,
559
- current.y + cmd.y
560
- );
561
- control.x = current.x + cmd.x2;
562
- control.y = current.y + cmd.y2;
563
- current.add(cmd);
564
- } else {
565
- path.bezierCurveTo(
566
- getReflection(current.x, control.x),
567
- getReflection(current.y, control.y),
568
- cmd.x2,
569
- cmd.y2,
570
- cmd.x,
571
- cmd.y
572
- );
573
- control.x = cmd.x2;
574
- control.y = cmd.y2;
575
- current.copyFrom(cmd);
576
- }
577
- } else if (cmd.type === "q" || cmd.type === "Q") {
578
- if (cmd.type === "q") {
579
- path.quadraticCurveTo(
580
- current.x + cmd.x1,
581
- current.y + cmd.y1,
582
- current.x + cmd.x,
583
- current.y + cmd.y
584
- );
585
- control.x = current.x + cmd.x1;
586
- control.y = current.y + cmd.y1;
587
- current.add(cmd);
588
- } else {
589
- path.quadraticCurveTo(
590
- cmd.x1,
591
- cmd.y1,
592
- cmd.x,
593
- cmd.y
594
- );
595
- control.x = cmd.x1;
596
- control.y = cmd.y1;
597
- current.copyFrom(cmd);
598
- }
599
- } else if (cmd.type === "t" || cmd.type === "T") {
600
- const rx = getReflection(current.x, control.x);
601
- const ry = getReflection(current.y, control.y);
602
- control.x = rx;
603
- control.y = ry;
604
- if (cmd.type === "t") {
605
- path.quadraticCurveTo(
606
- rx,
607
- ry,
608
- current.x + cmd.x,
609
- current.y + cmd.y
610
- );
611
- current.add(cmd);
612
- } else {
613
- path.quadraticCurveTo(
614
- rx,
615
- ry,
616
- cmd.x,
617
- cmd.y
618
- );
619
- current.copyFrom(cmd);
427
+ function cubicBezierP1(t, p) {
428
+ const k = 1 - t;
429
+ return 3 * k * k * t * p;
430
+ }
431
+ function cubicBezierP2(t, p) {
432
+ return 3 * (1 - t) * t * t * p;
433
+ }
434
+ function cubicBezierP3(t, p) {
435
+ return t * t * t * p;
436
+ }
437
+ function cubicBezier(t, p0, p1, p2, p3) {
438
+ return cubicBezierP0(t, p0) + cubicBezierP1(t, p1) + cubicBezierP2(t, p2) + cubicBezierP3(t, p3);
439
+ }
440
+
441
+ function fillTriangulate(pointArray, options = {}) {
442
+ let {
443
+ vertices = [],
444
+ indices = [],
445
+ holes = [],
446
+ verticesStride = 2,
447
+ verticesOffset = vertices.length / verticesStride,
448
+ indicesOffset = indices.length
449
+ } = options;
450
+ const triangles = earcut__default(pointArray, holes, 2);
451
+ if (triangles.length) {
452
+ for (let i = 0; i < triangles.length; i += 3) {
453
+ indices[indicesOffset++] = triangles[i] + verticesOffset;
454
+ indices[indicesOffset++] = triangles[i + 1] + verticesOffset;
455
+ indices[indicesOffset++] = triangles[i + 2] + verticesOffset;
456
+ }
457
+ let index = verticesOffset * verticesStride;
458
+ for (let i = 0; i < pointArray.length; i += 2) {
459
+ vertices[index] = pointArray[i];
460
+ vertices[index + 1] = pointArray[i + 1];
461
+ index += verticesStride;
462
+ }
463
+ }
464
+ return {
465
+ vertices,
466
+ indices
467
+ };
468
+ }
469
+
470
+ const RECURSION_LIMIT$1 = 8;
471
+ const FLT_EPSILON$1 = 11920929e-14;
472
+ const PATH_DISTANCE_EPSILON$1 = 1;
473
+ function getAdaptiveCubicBezierCurvePoints(sX, sY, x1, y1, x2, y2, x, y, smoothness = 0.5, points = []) {
474
+ const scale = 1;
475
+ const smoothing = Math.min(
476
+ 0.99,
477
+ // a value of 1.0 actually inverts smoothing, so we cap it at 0.99
478
+ Math.max(0, smoothness)
479
+ );
480
+ let distanceTolerance = (PATH_DISTANCE_EPSILON$1 - smoothing) / scale;
481
+ distanceTolerance *= distanceTolerance;
482
+ recursive$1(sX, sY, x1, y1, x2, y2, x, y, points, distanceTolerance, 0);
483
+ points.push(x, y);
484
+ return points;
485
+ }
486
+ function recursive$1(x1, y1, x2, y2, x3, y3, x4, y4, points, distanceTolerance, level) {
487
+ if (level > RECURSION_LIMIT$1)
488
+ return;
489
+ const x12 = (x1 + x2) / 2;
490
+ const y12 = (y1 + y2) / 2;
491
+ const x23 = (x2 + x3) / 2;
492
+ const y23 = (y2 + y3) / 2;
493
+ const x34 = (x3 + x4) / 2;
494
+ const y34 = (y3 + y4) / 2;
495
+ const x123 = (x12 + x23) / 2;
496
+ const y123 = (y12 + y23) / 2;
497
+ const x234 = (x23 + x34) / 2;
498
+ const y234 = (y23 + y34) / 2;
499
+ const x1234 = (x123 + x234) / 2;
500
+ const y1234 = (y123 + y234) / 2;
501
+ if (level > 0) {
502
+ let dx = x4 - x1;
503
+ let dy = y4 - y1;
504
+ const d2 = Math.abs((x2 - x4) * dy - (y2 - y4) * dx);
505
+ const d3 = Math.abs((x3 - x4) * dy - (y3 - y4) * dx);
506
+ if (d2 > FLT_EPSILON$1 && d3 > FLT_EPSILON$1) {
507
+ if ((d2 + d3) * (d2 + d3) <= distanceTolerance * (dx * dx + dy * dy)) {
508
+ {
509
+ points.push(x1234, y1234);
510
+ return;
511
+ }
620
512
  }
621
- } else if (cmd.type === "a" || cmd.type === "A") {
622
- const start = current.clone();
623
- if (cmd.type === "a") {
624
- if (cmd.x === 0 && cmd.y === 0)
625
- continue;
626
- current.add(cmd);
627
- } else {
628
- if (current.equals(cmd))
629
- continue;
630
- current.copyFrom(cmd);
513
+ } else if (d2 > FLT_EPSILON$1) {
514
+ if (d2 * d2 <= distanceTolerance * (dx * dx + dy * dy)) {
515
+ {
516
+ points.push(x1234, y1234);
517
+ return;
518
+ }
631
519
  }
632
- control.copyFrom(current);
633
- parseArcCommand(
634
- path,
635
- cmd.rx,
636
- cmd.ry,
637
- cmd.angle,
638
- cmd.largeArcFlag,
639
- cmd.sweepFlag,
640
- start,
641
- current
642
- );
643
- } else if (cmd.type === "z" || cmd.type === "Z") {
644
- if (path.startPoint) {
645
- current.copyFrom(path.startPoint);
520
+ } else if (d3 > FLT_EPSILON$1) {
521
+ if (d3 * d3 <= distanceTolerance * (dx * dx + dy * dy)) {
522
+ {
523
+ points.push(x1234, y1234);
524
+ return;
525
+ }
646
526
  }
647
- path.closePath();
648
527
  } else {
649
- console.warn("Unsupported commands", cmd);
528
+ dx = x1234 - (x1 + x4) / 2;
529
+ dy = y1234 - (y1 + y4) / 2;
530
+ if (dx * dx + dy * dy <= distanceTolerance) {
531
+ points.push(x1234, y1234);
532
+ return;
533
+ }
650
534
  }
651
535
  }
536
+ recursive$1(x1, y1, x12, y12, x123, y123, x1234, y1234, points, distanceTolerance, level + 1);
537
+ recursive$1(x1234, y1234, x234, y234, x34, y34, x4, y4, points, distanceTolerance, level + 1);
652
538
  }
653
539
 
654
- function svgPathCommandsToData(commands) {
655
- let first;
656
- let prev;
657
- const data = [];
658
- for (let i = 0, len = commands.length; i < len; i++) {
659
- const cmd = commands[i];
660
- switch (cmd.type) {
661
- case "m":
662
- case "M":
663
- if (cmd.x.toFixed(4) === prev?.x.toFixed(4) && cmd.y.toFixed(4) === prev?.y.toFixed(4)) {
664
- continue;
665
- }
666
- data.push(`${cmd.type} ${cmd.x} ${cmd.y}`);
667
- prev = { x: cmd.x, y: cmd.y };
668
- first = { x: cmd.x, y: cmd.y };
669
- break;
670
- case "h":
671
- case "H":
672
- data.push(`${cmd.type} ${cmd.x}`);
673
- prev = { x: cmd.x, y: prev?.y ?? 0 };
674
- break;
675
- case "v":
676
- case "V":
677
- data.push(`${cmd.type} ${cmd.y}`);
678
- prev = { x: prev?.x ?? 0, y: cmd.y };
679
- break;
680
- case "l":
681
- case "L":
682
- data.push(`${cmd.type} ${cmd.x} ${cmd.y}`);
683
- prev = { x: cmd.x, y: cmd.y };
684
- break;
685
- case "c":
686
- case "C":
687
- data.push(`${cmd.type} ${cmd.x1} ${cmd.y1} ${cmd.x2} ${cmd.y2} ${cmd.x} ${cmd.y}`);
688
- prev = { x: cmd.x, y: cmd.y };
689
- break;
690
- case "s":
691
- case "S":
692
- data.push(`${cmd.type} ${cmd.x2} ${cmd.y2} ${cmd.x} ${cmd.y}`);
693
- prev = { x: cmd.x, y: cmd.y };
694
- break;
695
- case "q":
696
- case "Q":
697
- data.push(`${cmd.type} ${cmd.x1} ${cmd.y1} ${cmd.x} ${cmd.y}`);
698
- prev = { x: cmd.x, y: cmd.y };
699
- break;
700
- case "t":
701
- case "T":
702
- data.push(`${cmd.type} ${cmd.x} ${cmd.y}`);
703
- prev = { x: cmd.x, y: cmd.y };
704
- break;
705
- case "a":
706
- case "A":
707
- data.push(`${cmd.type} ${cmd.rx} ${cmd.ry} ${cmd.angle} ${cmd.largeArcFlag} ${cmd.sweepFlag} ${cmd.x} ${cmd.y}`);
708
- prev = { x: cmd.x, y: cmd.y };
709
- break;
710
- case "z":
711
- case "Z":
712
- data.push(cmd.type);
713
- if (first) {
714
- prev = { x: first.x, y: first.y };
715
- }
716
- break;
540
+ const RECURSION_LIMIT = 8;
541
+ const FLT_EPSILON = 11920929e-14;
542
+ const PATH_DISTANCE_EPSILON = 1;
543
+ function getAdaptiveQuadraticBezierCurvePoints(sX, sY, x1, y1, x, y, smoothness = 0.5, points = []) {
544
+ const scale = 1;
545
+ const smoothing = Math.min(
546
+ 0.99,
547
+ // a value of 1.0 actually inverts smoothing, so we cap it at 0.99
548
+ Math.max(0, smoothness)
549
+ );
550
+ let distanceTolerance = (PATH_DISTANCE_EPSILON - smoothing) / scale;
551
+ distanceTolerance *= distanceTolerance;
552
+ recursive(points, sX, sY, x1, y1, x, y, distanceTolerance, 0);
553
+ points.push(x, y);
554
+ return points;
555
+ }
556
+ function recursive(points, x1, y1, x2, y2, x3, y3, distanceTolerance, level) {
557
+ if (level > RECURSION_LIMIT)
558
+ return;
559
+ const x12 = (x1 + x2) / 2;
560
+ const y12 = (y1 + y2) / 2;
561
+ const x23 = (x2 + x3) / 2;
562
+ const y23 = (y2 + y3) / 2;
563
+ const x123 = (x12 + x23) / 2;
564
+ const y123 = (y12 + y23) / 2;
565
+ let dx = x3 - x1;
566
+ let dy = y3 - y1;
567
+ const d = Math.abs((x2 - x3) * dy - (y2 - y3) * dx);
568
+ if (d > FLT_EPSILON) {
569
+ if (d * d <= distanceTolerance * (dx * dx + dy * dy)) {
570
+ {
571
+ points.push(x123, y123);
572
+ return;
573
+ }
574
+ }
575
+ } else {
576
+ dx = x123 - (x1 + x3) / 2;
577
+ dy = y123 - (y1 + y3) / 2;
578
+ if (dx * dx + dy * dy <= distanceTolerance) {
579
+ points.push(x123, y123);
580
+ return;
717
581
  }
718
582
  }
719
- return data.join(" ");
583
+ recursive(points, x1, y1, x12, y12, x123, y123, distanceTolerance, level + 1);
584
+ recursive(points, x123, y123, x23, y23, x3, y3, distanceTolerance, level + 1);
720
585
  }
721
586
 
722
- const RE$2 = /[a-df-z][^a-df-z]*/gi;
723
- function svgPathDataToCommands(data) {
724
- const commands = [];
725
- const matched = data.match(RE$2);
726
- if (!matched) {
727
- return commands;
728
- }
729
- for (let i = 0, len = matched.length; i < len; i++) {
730
- const command = matched[i];
731
- const type = command.charAt(0);
732
- const data2 = command.slice(1).trim();
733
- let args;
734
- switch (type) {
735
- case "m":
736
- case "M":
737
- args = parsePathDataArgs(data2);
738
- for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 2) {
739
- if (i2 === 0) {
740
- commands.push({ type, x: args[i2], y: args[i2 + 1] });
741
- } else {
742
- commands.push({ type: type === "m" ? "l" : "L", x: args[i2], y: args[i2 + 1] });
743
- }
744
- }
745
- break;
746
- case "h":
747
- case "H":
748
- args = parsePathDataArgs(data2);
749
- for (let i2 = 0, len2 = args.length; i2 < len2; i2++) {
750
- commands.push({ type, x: args[i2] });
751
- }
752
- break;
753
- case "v":
754
- case "V":
755
- args = parsePathDataArgs(data2);
756
- for (let i2 = 0, len2 = args.length; i2 < len2; i2++) {
757
- commands.push({ type, y: args[i2] });
758
- }
759
- break;
760
- case "l":
761
- case "L":
762
- args = parsePathDataArgs(data2);
763
- for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 2) {
764
- commands.push({ type, x: args[i2], y: args[i2 + 1] });
765
- }
766
- break;
767
- case "c":
768
- case "C":
769
- args = parsePathDataArgs(data2);
770
- for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 6) {
771
- commands.push({
772
- type,
773
- x1: args[i2],
774
- y1: args[i2 + 1],
775
- x2: args[i2 + 2],
776
- y2: args[i2 + 3],
777
- x: args[i2 + 4],
778
- y: args[i2 + 5]
779
- });
780
- }
781
- break;
782
- case "s":
783
- case "S":
784
- args = parsePathDataArgs(data2);
785
- for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 4) {
786
- commands.push({
787
- type,
788
- x2: args[i2],
789
- y2: args[i2 + 1],
790
- x: args[i2 + 2],
791
- y: args[i2 + 3]
792
- });
793
- }
794
- break;
795
- case "q":
796
- case "Q":
797
- args = parsePathDataArgs(data2);
798
- for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 4) {
799
- commands.push({
800
- type,
801
- x1: args[i2],
802
- y1: args[i2 + 1],
803
- x: args[i2 + 2],
804
- y: args[i2 + 3]
805
- });
806
- }
807
- break;
808
- case "t":
809
- case "T":
810
- args = parsePathDataArgs(data2);
811
- for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 2) {
812
- commands.push({
813
- type,
814
- x: args[i2],
815
- y: args[i2 + 1]
816
- });
817
- }
818
- break;
819
- case "a":
820
- case "A":
821
- args = parsePathDataArgs(data2, [3, 4], 7);
822
- for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 7) {
823
- commands.push({
824
- type,
825
- rx: args[i2],
826
- ry: args[i2 + 1],
827
- angle: args[i2 + 2],
828
- largeArcFlag: args[i2 + 3],
829
- sweepFlag: args[i2 + 4],
830
- x: args[i2 + 5],
831
- y: args[i2 + 6]
832
- });
833
- }
834
- break;
835
- case "z":
836
- case "Z":
837
- commands.push({
838
- type
839
- });
840
- break;
841
- default:
842
- console.warn(command);
843
- }
587
+ function getDirectedArea(vertices) {
588
+ let area = 0;
589
+ const n = vertices.length;
590
+ for (let i = 0; i < n; i += 2) {
591
+ const x0 = vertices[i];
592
+ const y0 = vertices[i + 1];
593
+ const x1 = vertices[(i + 2) % (n - 1)];
594
+ const y1 = vertices[(i + 3) % n];
595
+ area += x0 * y1 - x1 * y0;
844
596
  }
845
- return commands;
597
+ return area / 2;
846
598
  }
847
599
 
848
- const dataUri = "data:image/svg+xml;";
849
- const base64DataUri = `${dataUri}base64,`;
850
- const utf8DataUri = `${dataUri}charset=utf8,`;
851
- function svgToDom(svg) {
852
- if (typeof svg === "string") {
853
- let xml;
854
- if (svg.startsWith(base64DataUri)) {
855
- svg = svg.substring(base64DataUri.length, svg.length);
856
- xml = atob(svg);
857
- } else if (svg.startsWith(utf8DataUri)) {
858
- svg = svg.substring(utf8DataUri.length, svg.length);
859
- xml = decodeURIComponent(svg);
600
+ function cross(ax, ay, bx, by, cx, cy) {
601
+ return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
602
+ }
603
+ function windingNumber(px, py, polygon) {
604
+ const polygonLen = polygon.length;
605
+ let wn = 0;
606
+ for (let i = 0, j = polygonLen - 2; i < polygonLen; j = i, i += 2) {
607
+ const xi = polygon[i];
608
+ const yi = polygon[i + 1];
609
+ const xj = polygon[j];
610
+ const yj = polygon[j + 1];
611
+ if (yi <= py) {
612
+ if (yj > py && cross(xj, yj, xi, yi, px, py) > 0)
613
+ wn++;
860
614
  } else {
861
- xml = svg;
862
- }
863
- const doc = new DOMParser().parseFromString(xml, "text/xml");
864
- const error = doc.querySelector("parsererror");
865
- if (error) {
866
- throw new Error(`${error.textContent ?? "parser error"}
867
- ${xml}`);
615
+ if (yj <= py && cross(xj, yj, xi, yi, px, py) < 0)
616
+ wn--;
868
617
  }
869
- return doc.documentElement;
870
- } else {
871
- return svg;
872
618
  }
619
+ return wn;
873
620
  }
874
-
875
- const defaultUnit = "px";
876
- const defaultDPI = 90;
877
- const units = ["mm", "cm", "in", "pt", "pc", "px"];
878
- const unitConversion = {
879
- mm: {
880
- mm: 1,
881
- cm: 0.1,
882
- in: 1 / 25.4,
883
- pt: 72 / 25.4,
884
- pc: 6 / 25.4,
885
- px: -1
886
- },
887
- cm: {
888
- mm: 10,
889
- cm: 1,
890
- in: 1 / 2.54,
891
- pt: 72 / 2.54,
892
- pc: 6 / 2.54,
893
- px: -1
894
- },
895
- in: {
896
- mm: 25.4,
897
- cm: 2.54,
898
- in: 1,
899
- pt: 72,
900
- pc: 6,
901
- px: -1
902
- },
903
- pt: {
904
- mm: 25.4 / 72,
905
- cm: 2.54 / 72,
906
- in: 1 / 72,
907
- pt: 1,
908
- pc: 6 / 72,
909
- px: -1
910
- },
911
- pc: {
912
- mm: 25.4 / 6,
913
- cm: 2.54 / 6,
914
- in: 1 / 6,
915
- pt: 72 / 6,
916
- pc: 1,
917
- px: -1
918
- },
919
- px: {
920
- px: 1
921
- }
922
- };
923
- function parseFloatWithUnits(string) {
924
- let theUnit = "px";
925
- if (typeof string === "string") {
926
- for (let i = 0, n = units.length; i < n; i++) {
927
- const u = units[i];
928
- if (string.endsWith(u)) {
929
- theUnit = u;
930
- string = string.substring(0, string.length - u.length);
931
- break;
621
+ function distance(p1, p2) {
622
+ const dx = p2[0] - p1[0];
623
+ const dy = p2[1] - p1[1];
624
+ return Math.sqrt(dx * dx + dy * dy);
625
+ }
626
+ function nonzeroFillRule(paths) {
627
+ const results = paths.map((_, i) => ({ index: i }));
628
+ const testPointsGroups = paths.map((path) => {
629
+ const len = path.length;
630
+ if (!len) {
631
+ return [];
632
+ }
633
+ let xMinYAuto = [Number.MAX_SAFE_INTEGER, 0];
634
+ let xAutoYMin = [0, Number.MAX_SAFE_INTEGER];
635
+ let xMaxYAuto = [Number.MIN_SAFE_INTEGER, 0];
636
+ let xAutoYMax = [0, Number.MIN_SAFE_INTEGER];
637
+ for (let i = 0; i < len; i += 2) {
638
+ const x = path[i];
639
+ const y = path[i + 1];
640
+ if (xMinYAuto[0] > x) {
641
+ xMinYAuto = [x, y];
642
+ }
643
+ if (xAutoYMin[1] > y) {
644
+ xAutoYMin = [x, y];
645
+ }
646
+ if (xMaxYAuto[0] < x) {
647
+ xMaxYAuto = [x, y];
648
+ }
649
+ if (xAutoYMax[1] < y) {
650
+ xAutoYMax = [x, y];
932
651
  }
933
652
  }
934
- }
935
- let scale;
936
- {
937
- scale = unitConversion[theUnit][defaultUnit];
938
- if (scale < 0) {
939
- scale = unitConversion[theUnit].in * defaultDPI;
653
+ const mid = [
654
+ (xMinYAuto[0] + xMaxYAuto[0]) / 2,
655
+ (xAutoYMin[1] + xAutoYMax[1]) / 2
656
+ ];
657
+ let xMidYMinDx;
658
+ let xMidYMaxDx;
659
+ let xMidYMin;
660
+ let xMidYMax;
661
+ let xMinYMidDy;
662
+ let xMaxYMidDy;
663
+ let xMinYMid;
664
+ let xMaxYMid;
665
+ for (let i = 0; i < len; i += 2) {
666
+ const x = path[i];
667
+ const y = path[i + 1];
668
+ const _dx = Math.abs(x - mid[0]);
669
+ const _dy = Math.abs(y - mid[1]);
670
+ if (y < mid[1] && (!xMidYMinDx || _dx < xMidYMinDx)) {
671
+ xMidYMinDx = _dx;
672
+ xMidYMin = [x, y];
673
+ }
674
+ if (y > mid[1] && (!xMidYMaxDx || _dx < xMidYMaxDx)) {
675
+ xMidYMaxDx = _dx;
676
+ xMidYMax = [x, y];
677
+ }
678
+ if (x < mid[0] && (!xMinYMidDy || _dy < xMinYMidDy)) {
679
+ xMinYMidDy = _dy;
680
+ xMinYMid = [x, y];
681
+ }
682
+ if (x > mid[0] && (!xMaxYMidDy || _dy < xMaxYMidDy)) {
683
+ xMaxYMidDy = _dy;
684
+ xMaxYMid = [x, y];
685
+ }
940
686
  }
941
- }
942
- return scale * Number.parseFloat(string);
943
- }
944
-
945
- function getNodeTransform(node, currentTransform, transformStack) {
946
- if (!(node.hasAttribute("transform") || node.nodeName === "use" && (node.hasAttribute("x") || node.hasAttribute("y")))) {
947
- return null;
948
- }
949
- const transform = parseNodeTransform(node);
950
- if (transformStack.length > 0) {
951
- transform.prepend(transformStack[transformStack.length - 1]);
952
- }
953
- currentTransform.copyFrom(transform);
954
- transformStack.push(transform);
955
- return transform;
956
- }
957
- function parseNodeTransform(node) {
958
- const transform = new Transform2D();
959
- if (node.nodeName === "use" && (node.hasAttribute("x") || node.hasAttribute("y"))) {
960
- transform.translate(
961
- parseFloatWithUnits(node.getAttribute("x")),
962
- parseFloatWithUnits(node.getAttribute("y"))
963
- );
964
- }
965
- if (node.hasAttribute("transform")) {
966
- transform.appendCssTransform(node.getAttribute("transform"));
967
- }
968
- return transform;
969
- }
970
-
971
- function parseCircleNode(node) {
972
- return new Path2D().arc(
973
- parseFloatWithUnits(node.getAttribute("cx") || 0),
974
- parseFloatWithUnits(node.getAttribute("cy") || 0),
975
- parseFloatWithUnits(node.getAttribute("r") || 0),
976
- 0,
977
- Math.PI * 2
978
- );
979
- }
980
-
981
- function parseCSSStylesheet(node, stylesheets) {
982
- if (!node.sheet || !node.sheet.cssRules || !node.sheet.cssRules.length)
983
- return;
984
- for (let i = 0; i < node.sheet.cssRules.length; i++) {
985
- const stylesheet = node.sheet.cssRules[i];
986
- if (stylesheet.type !== 1)
987
- continue;
988
- const selectorList = stylesheet.selectorText.split(/,/g).filter(Boolean).map((i2) => i2.trim());
989
- const definitions = {};
990
- for (let len = stylesheet.style.length, i2 = 0; i2 < len; i2++) {
991
- const name = stylesheet.style.item(i2);
992
- definitions[name] = stylesheet.style.getPropertyValue(name);
687
+ return [
688
+ xMinYAuto,
689
+ xAutoYMin,
690
+ xMaxYAuto,
691
+ xAutoYMax,
692
+ xMidYMin,
693
+ xMidYMax,
694
+ xMinYMid,
695
+ xMaxYMid
696
+ ].filter(Boolean);
697
+ });
698
+ for (let i = 0, len = paths.length; i < len; i++) {
699
+ const _results = [];
700
+ const testPoints = testPointsGroups[i];
701
+ for (let j = 0; j < len; j++) {
702
+ if (i === j)
703
+ continue;
704
+ const wnMap = {};
705
+ const wnList = [];
706
+ for (let p = 0, pLen = testPoints.length; p < pLen; p++) {
707
+ const [x, y] = testPoints[p];
708
+ const winding = windingNumber(x, y, paths[j]);
709
+ wnMap[winding] = (wnMap[winding] ?? 0) + 1;
710
+ wnList.push(winding);
711
+ }
712
+ if (wnList.filter((v) => v !== 0).length > wnList.filter((v) => v === 0).length) {
713
+ _results.push({
714
+ index: i,
715
+ parentIndex: j,
716
+ winding: Number(
717
+ Array.from(Object.entries(wnMap)).sort((a, b) => b[1] - a[1])?.[0]?.[0] ?? 0
718
+ ),
719
+ dist: distance(testPointsGroups[i][0], testPointsGroups[j][0])
720
+ });
721
+ }
993
722
  }
994
- for (let j = 0; j < selectorList.length; j++) {
995
- stylesheets[selectorList[j]] = Object.assign(
996
- stylesheets[selectorList[j]] || {},
997
- { ...definitions }
998
- );
723
+ if (_results.reduce((total, item) => total + item.winding, 0) !== 0) {
724
+ _results.sort((a, b) => a.dist - b.dist);
725
+ results[i] = _results[0];
999
726
  }
1000
727
  }
728
+ return results;
1001
729
  }
1002
730
 
1003
- function parseEllipseNode(node) {
1004
- return new Path2D().ellipse(
1005
- parseFloatWithUnits(node.getAttribute("cx") || 0),
1006
- parseFloatWithUnits(node.getAttribute("cy") || 0),
1007
- parseFloatWithUnits(node.getAttribute("rx") || 0),
1008
- parseFloatWithUnits(node.getAttribute("ry") || 0),
1009
- 0,
1010
- 0,
1011
- Math.PI * 2
1012
- );
731
+ function quadraticBezierP0(t, p) {
732
+ const k = 1 - t;
733
+ return k * k * p;
1013
734
  }
1014
-
1015
- function parseLineNode(node) {
1016
- return new Path2D().moveTo(
1017
- parseFloatWithUnits(node.getAttribute("x1") || 0),
1018
- parseFloatWithUnits(node.getAttribute("y1") || 0)
1019
- ).lineTo(
1020
- parseFloatWithUnits(node.getAttribute("x2") || 0),
1021
- parseFloatWithUnits(node.getAttribute("y2") || 0)
1022
- );
735
+ function quadraticBezierP1(t, p) {
736
+ return 2 * (1 - t) * t * p;
1023
737
  }
1024
-
1025
- function parsePathNode(node) {
1026
- const path = new Path2D();
1027
- const d = node.getAttribute("d");
1028
- if (!d || d === "none")
1029
- return null;
1030
- path.addData(d);
1031
- return path;
738
+ function quadraticBezierP2(t, p) {
739
+ return t * t * p;
740
+ }
741
+ function quadraticBezier(t, p0, p1, p2) {
742
+ return quadraticBezierP0(t, p0) + quadraticBezierP1(t, p1) + quadraticBezierP2(t, p2);
1032
743
  }
1033
744
 
1034
- const RE$1 = /([+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)(?:,|\s)([+-]?\d*\.?\d+(?:e[+-]?\d+)?)/g;
1035
- function parsePolygonNode(node) {
1036
- const path = new Path2D();
1037
- let index = 0;
1038
- node.getAttribute("points")?.replace(RE$1, (match, a, b) => {
1039
- const x = parseFloatWithUnits(a);
1040
- const y = parseFloatWithUnits(b);
1041
- if (index === 0) {
1042
- path.moveTo(x, y);
1043
- } else {
1044
- path.lineTo(x, y);
1045
- }
1046
- index++;
1047
- return match;
1048
- });
1049
- path.currentCurve.autoClose = true;
1050
- return path;
1051
- }
1052
-
1053
- const RE = /([+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)(?:,|\s)([+-]?\d*\.?\d+(?:e[+-]?\d+)?)/g;
1054
- function parsePolylineNode(node) {
1055
- const path = new Path2D();
1056
- let index = 0;
1057
- node.getAttribute("points")?.replace(RE, (match, a, b) => {
1058
- const x = parseFloatWithUnits(a);
1059
- const y = parseFloatWithUnits(b);
1060
- if (index === 0) {
1061
- path.moveTo(x, y);
1062
- } else {
1063
- path.lineTo(x, y);
1064
- }
1065
- index++;
1066
- return match;
1067
- });
1068
- path.currentCurve.autoClose = false;
1069
- return path;
1070
- }
1071
-
1072
- function parseRectNode(node) {
1073
- const x = parseFloatWithUnits(node.getAttribute("x") || 0);
1074
- const y = parseFloatWithUnits(node.getAttribute("y") || 0);
1075
- const rx = parseFloatWithUnits(node.getAttribute("rx") || node.getAttribute("ry") || 0);
1076
- const ry = parseFloatWithUnits(node.getAttribute("ry") || node.getAttribute("rx") || 0);
1077
- const w = parseFloatWithUnits(node.getAttribute("width"));
1078
- const h = parseFloatWithUnits(node.getAttribute("height"));
1079
- const bci = 1 - 0.551915024494;
1080
- const path = new Path2D();
1081
- path.moveTo(x + rx, y);
1082
- path.lineTo(x + w - rx, y);
1083
- if (rx !== 0 || ry !== 0) {
1084
- path.bezierCurveTo(
1085
- x + w - rx * bci,
1086
- y,
1087
- x + w,
1088
- y + ry * bci,
1089
- x + w,
1090
- y + ry
1091
- );
1092
- }
1093
- path.lineTo(x + w, y + h - ry);
1094
- if (rx !== 0 || ry !== 0) {
1095
- path.bezierCurveTo(
1096
- x + w,
1097
- y + h - ry * bci,
1098
- x + w - rx * bci,
1099
- y + h,
1100
- x + w - rx,
1101
- y + h
1102
- );
1103
- }
1104
- path.lineTo(x + rx, y + h);
1105
- if (rx !== 0 || ry !== 0) {
1106
- path.bezierCurveTo(
1107
- x + rx * bci,
1108
- y + h,
1109
- x,
1110
- y + h - ry * bci,
1111
- x,
1112
- y + h - ry
1113
- );
745
+ const closePointEps = 1e-4;
746
+ const curveEps = 1e-4;
747
+ function strokeTriangulate(points, options = {}) {
748
+ const {
749
+ vertices = [],
750
+ indices = [],
751
+ lineStyle = {
752
+ alignment: 0.5,
753
+ cap: "butt",
754
+ join: "miter",
755
+ width: 1,
756
+ miterLimit: 10
757
+ },
758
+ flipAlignment = false,
759
+ closed = true
760
+ } = options;
761
+ const eps = closePointEps;
762
+ if (points.length === 0) {
763
+ return { vertices, indices };
1114
764
  }
1115
- path.lineTo(x, y + ry);
1116
- if (rx !== 0 || ry !== 0) {
1117
- path.bezierCurveTo(x, y + ry * bci, x + rx * bci, y, x + rx, y);
765
+ const style = lineStyle;
766
+ let alignment = style.alignment;
767
+ if (lineStyle.alignment !== 0.5) {
768
+ let orientation = getOrientationOfPoints(points);
769
+ if (flipAlignment)
770
+ orientation *= -1;
771
+ alignment = (alignment - 0.5) * orientation + 0.5;
1118
772
  }
1119
- return path;
1120
- }
1121
-
1122
- function parseStyle(node, style, stylesheets) {
1123
- style = Object.assign({}, style);
1124
- let stylesheetStyles = {};
1125
- if (node.hasAttribute("class")) {
1126
- const classSelectors = node.getAttribute("class").split(/\s/).filter(Boolean).map((i) => i.trim());
1127
- for (let i = 0; i < classSelectors.length; i++) {
1128
- stylesheetStyles = Object.assign(stylesheetStyles, stylesheets[`.${classSelectors[i]}`]);
773
+ const firstPoint = { x: points[0], y: points[1] };
774
+ const lastPoint = { x: points[points.length - 2], y: points[points.length - 1] };
775
+ const closedShape = closed;
776
+ const closedPath = Math.abs(firstPoint.x - lastPoint.x) < eps && Math.abs(firstPoint.y - lastPoint.y) < eps;
777
+ if (closedShape) {
778
+ points = points.slice();
779
+ if (closedPath) {
780
+ points.pop();
781
+ points.pop();
782
+ lastPoint.x = points[points.length - 2];
783
+ lastPoint.y = points[points.length - 1];
1129
784
  }
785
+ const midPointX = (firstPoint.x + lastPoint.x) * 0.5;
786
+ const midPointY = (lastPoint.y + firstPoint.y) * 0.5;
787
+ points.unshift(midPointX, midPointY);
788
+ points.push(midPointX, midPointY);
1130
789
  }
1131
- if (node.hasAttribute("id")) {
1132
- stylesheetStyles = Object.assign(stylesheetStyles, stylesheets[`#${node.getAttribute("id")}`]);
1133
- }
1134
- for (let len = node.style.length, i = 0; i < len; i++) {
1135
- const name = node.style.item(i);
1136
- const value = node.style.getPropertyValue(name);
1137
- style[name] = value;
1138
- stylesheetStyles[name] = value;
1139
- }
1140
- function addStyle(svgName, jsName, adjustFunction = copy) {
1141
- if (node.hasAttribute(svgName))
1142
- style[jsName] = adjustFunction(node.getAttribute(svgName));
1143
- if (stylesheetStyles[svgName])
1144
- style[jsName] = adjustFunction(stylesheetStyles[svgName]);
1145
- }
1146
- function copy(v) {
1147
- if (v.startsWith("url"))
1148
- console.warn("url access in attributes is not implemented.");
1149
- return v;
1150
- }
1151
- function clamp(v) {
1152
- return Math.max(0, Math.min(1, parseFloatWithUnits(v)));
1153
- }
1154
- function positive(v) {
1155
- return Math.max(0, parseFloatWithUnits(v));
1156
- }
1157
- function array(v) {
1158
- return v.split(" ").filter((v2) => v2 !== "").map((v2) => parseFloatWithUnits(v2));
790
+ const verts = vertices;
791
+ const length = points.length / 2;
792
+ let indexCount = points.length;
793
+ const indexStart = verts.length / 2;
794
+ const width = style.width / 2;
795
+ const widthSquared = width * width;
796
+ const miterLimitSquared = style.miterLimit * style.miterLimit;
797
+ let x0 = points[0];
798
+ let y0 = points[1];
799
+ let x1 = points[2];
800
+ let y1 = points[3];
801
+ let x2 = 0;
802
+ let y2 = 0;
803
+ let perpX = -(y0 - y1);
804
+ let perpY = x0 - x1;
805
+ let perp1x = 0;
806
+ let perp1y = 0;
807
+ let dist = Math.sqrt(perpX * perpX + perpY * perpY);
808
+ perpX /= dist;
809
+ perpY /= dist;
810
+ perpX *= width;
811
+ perpY *= width;
812
+ const ratio = alignment;
813
+ const innerWeight = (1 - ratio) * 2;
814
+ const outerWeight = ratio * 2;
815
+ if (!closedShape) {
816
+ if (style.cap === "round") {
817
+ indexCount += round(
818
+ x0 - perpX * (innerWeight - outerWeight) * 0.5,
819
+ y0 - perpY * (innerWeight - outerWeight) * 0.5,
820
+ x0 - perpX * innerWeight,
821
+ y0 - perpY * innerWeight,
822
+ x0 + perpX * outerWeight,
823
+ y0 + perpY * outerWeight,
824
+ verts,
825
+ true
826
+ ) + 2;
827
+ } else if (style.cap === "square") {
828
+ indexCount += square(x0, y0, perpX, perpY, innerWeight, outerWeight, true, verts);
829
+ }
1159
830
  }
1160
- addStyle("fill", "fill");
1161
- addStyle("fill-opacity", "fillOpacity", clamp);
1162
- addStyle("fill-rule", "fillRule");
1163
- addStyle("opacity", "opacity", clamp);
1164
- addStyle("stroke", "stroke");
1165
- addStyle("stroke-opacity", "strokeOpacity", clamp);
1166
- addStyle("stroke-width", "strokeWidth", positive);
1167
- addStyle("stroke-linecap", "strokeLinecap");
1168
- addStyle("stroke-linejoin", "strokeLinejoin");
1169
- addStyle("stroke-miterlimit", "strokeMiterlimit", positive);
1170
- addStyle("stroke-dasharray", "strokeDasharray", array);
1171
- addStyle("stroke-dashoffset", "strokeDashoffset", parseFloatWithUnits);
1172
- addStyle("visibility", "visibility");
1173
- return style;
1174
- }
1175
-
1176
- function parseNode(node, style, paths = [], stylesheets = {}) {
1177
- if (node.nodeType !== 1)
1178
- return paths;
1179
- let isDefsNode = false;
1180
- let path = null;
1181
- let _style = { ...style };
1182
- switch (node.nodeName) {
1183
- case "svg":
1184
- _style = parseStyle(node, _style, stylesheets);
1185
- break;
1186
- case "style":
1187
- parseCSSStylesheet(node, stylesheets);
1188
- break;
1189
- case "g":
1190
- _style = parseStyle(node, _style, stylesheets);
1191
- break;
1192
- case "path":
1193
- _style = parseStyle(node, _style, stylesheets);
1194
- if (node.hasAttribute("d"))
1195
- path = parsePathNode(node);
1196
- break;
1197
- case "rect":
1198
- _style = parseStyle(node, _style, stylesheets);
1199
- path = parseRectNode(node);
1200
- break;
1201
- case "polygon":
1202
- _style = parseStyle(node, _style, stylesheets);
1203
- path = parsePolygonNode(node);
1204
- break;
1205
- case "polyline":
1206
- _style = parseStyle(node, _style, stylesheets);
1207
- path = parsePolylineNode(node);
1208
- break;
1209
- case "circle":
1210
- _style = parseStyle(node, _style, stylesheets);
1211
- path = parseCircleNode(node);
1212
- break;
1213
- case "ellipse":
1214
- _style = parseStyle(node, _style, stylesheets);
1215
- path = parseEllipseNode(node);
1216
- break;
1217
- case "line":
1218
- _style = parseStyle(node, _style, stylesheets);
1219
- path = parseLineNode(node);
1220
- break;
1221
- case "defs":
1222
- isDefsNode = true;
1223
- break;
1224
- case "use": {
1225
- _style = parseStyle(node, _style, stylesheets);
1226
- const href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href") || node.getAttribute("href") || "";
1227
- const usedNodeId = href.substring(1);
1228
- const usedNode = node.viewportElement?.getElementById(usedNodeId);
1229
- if (usedNode) {
1230
- parseNode(usedNode, _style, paths, stylesheets);
831
+ verts.push(
832
+ x0 - perpX * innerWeight,
833
+ y0 - perpY * innerWeight
834
+ );
835
+ verts.push(
836
+ x0 + perpX * outerWeight,
837
+ y0 + perpY * outerWeight
838
+ );
839
+ for (let i = 1; i < length - 1; ++i) {
840
+ x0 = points[(i - 1) * 2];
841
+ y0 = points[(i - 1) * 2 + 1];
842
+ x1 = points[i * 2];
843
+ y1 = points[i * 2 + 1];
844
+ x2 = points[(i + 1) * 2];
845
+ y2 = points[(i + 1) * 2 + 1];
846
+ perpX = -(y0 - y1);
847
+ perpY = x0 - x1;
848
+ dist = Math.sqrt(perpX * perpX + perpY * perpY);
849
+ perpX /= dist;
850
+ perpY /= dist;
851
+ perpX *= width;
852
+ perpY *= width;
853
+ perp1x = -(y1 - y2);
854
+ perp1y = x1 - x2;
855
+ dist = Math.sqrt(perp1x * perp1x + perp1y * perp1y);
856
+ perp1x /= dist;
857
+ perp1y /= dist;
858
+ perp1x *= width;
859
+ perp1y *= width;
860
+ const dx0 = x1 - x0;
861
+ const dy0 = y0 - y1;
862
+ const dx1 = x1 - x2;
863
+ const dy1 = y2 - y1;
864
+ const dot = dx0 * dx1 + dy0 * dy1;
865
+ const cross = dy0 * dx1 - dy1 * dx0;
866
+ const clockwise = cross < 0;
867
+ if (Math.abs(cross) < 1e-3 * Math.abs(dot)) {
868
+ verts.push(
869
+ x1 - perpX * innerWeight,
870
+ y1 - perpY * innerWeight
871
+ );
872
+ verts.push(
873
+ x1 + perpX * outerWeight,
874
+ y1 + perpY * outerWeight
875
+ );
876
+ if (dot >= 0) {
877
+ if (style.join === "round") {
878
+ indexCount += round(
879
+ x1,
880
+ y1,
881
+ x1 - perpX * innerWeight,
882
+ y1 - perpY * innerWeight,
883
+ x1 - perp1x * innerWeight,
884
+ y1 - perp1y * innerWeight,
885
+ verts,
886
+ false
887
+ ) + 4;
888
+ } else {
889
+ indexCount += 2;
890
+ }
891
+ verts.push(
892
+ x1 - perp1x * outerWeight,
893
+ y1 - perp1y * outerWeight
894
+ );
895
+ verts.push(
896
+ x1 + perp1x * innerWeight,
897
+ y1 + perp1y * innerWeight
898
+ );
899
+ }
900
+ continue;
901
+ }
902
+ const c1 = (-perpX + x0) * (-perpY + y1) - (-perpX + x1) * (-perpY + y0);
903
+ const c2 = (-perp1x + x2) * (-perp1y + y1) - (-perp1x + x1) * (-perp1y + y2);
904
+ const px = (dx0 * c2 - dx1 * c1) / cross;
905
+ const py = (dy1 * c1 - dy0 * c2) / cross;
906
+ const pDist = (px - x1) * (px - x1) + (py - y1) * (py - y1);
907
+ const imx = x1 + (px - x1) * innerWeight;
908
+ const imy = y1 + (py - y1) * innerWeight;
909
+ const omx = x1 - (px - x1) * outerWeight;
910
+ const omy = y1 - (py - y1) * outerWeight;
911
+ const smallerInsideSegmentSq = Math.min(dx0 * dx0 + dy0 * dy0, dx1 * dx1 + dy1 * dy1);
912
+ const insideWeight = clockwise ? innerWeight : outerWeight;
913
+ const smallerInsideDiagonalSq = smallerInsideSegmentSq + insideWeight * insideWeight * widthSquared;
914
+ const insideMiterOk = pDist <= smallerInsideDiagonalSq;
915
+ if (insideMiterOk) {
916
+ if (style.join === "bevel" || pDist / widthSquared > miterLimitSquared) {
917
+ if (clockwise) {
918
+ verts.push(imx, imy);
919
+ verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
920
+ verts.push(imx, imy);
921
+ verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
922
+ } else {
923
+ verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
924
+ verts.push(omx, omy);
925
+ verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
926
+ verts.push(omx, omy);
927
+ }
928
+ indexCount += 2;
929
+ } else if (style.join === "round") {
930
+ if (clockwise) {
931
+ verts.push(imx, imy);
932
+ verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
933
+ indexCount += round(
934
+ x1,
935
+ y1,
936
+ x1 + perpX * outerWeight,
937
+ y1 + perpY * outerWeight,
938
+ x1 + perp1x * outerWeight,
939
+ y1 + perp1y * outerWeight,
940
+ verts,
941
+ true
942
+ ) + 4;
943
+ verts.push(imx, imy);
944
+ verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
945
+ } else {
946
+ verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
947
+ verts.push(omx, omy);
948
+ indexCount += round(
949
+ x1,
950
+ y1,
951
+ x1 - perpX * innerWeight,
952
+ y1 - perpY * innerWeight,
953
+ x1 - perp1x * innerWeight,
954
+ y1 - perp1y * innerWeight,
955
+ verts,
956
+ false
957
+ ) + 4;
958
+ verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
959
+ verts.push(omx, omy);
960
+ }
1231
961
  } else {
1232
- console.warn(`'use node' references non-existent node id: ${usedNodeId}`);
962
+ verts.push(imx, imy);
963
+ verts.push(omx, omy);
1233
964
  }
1234
- break;
965
+ } else {
966
+ verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
967
+ verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
968
+ if (style.join === "round") {
969
+ if (clockwise) {
970
+ indexCount += round(
971
+ x1,
972
+ y1,
973
+ x1 + perpX * outerWeight,
974
+ y1 + perpY * outerWeight,
975
+ x1 + perp1x * outerWeight,
976
+ y1 + perp1y * outerWeight,
977
+ verts,
978
+ true
979
+ ) + 2;
980
+ } else {
981
+ indexCount += round(
982
+ x1,
983
+ y1,
984
+ x1 - perpX * innerWeight,
985
+ y1 - perpY * innerWeight,
986
+ x1 - perp1x * innerWeight,
987
+ y1 - perp1y * innerWeight,
988
+ verts,
989
+ false
990
+ ) + 2;
991
+ }
992
+ } else if (style.join === "miter" && pDist / widthSquared <= miterLimitSquared) {
993
+ if (clockwise) {
994
+ verts.push(omx, omy);
995
+ verts.push(omx, omy);
996
+ } else {
997
+ verts.push(imx, imy);
998
+ verts.push(imx, imy);
999
+ }
1000
+ indexCount += 2;
1001
+ }
1002
+ verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
1003
+ verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
1004
+ indexCount += 2;
1235
1005
  }
1236
- default:
1237
- console.warn(node);
1238
- break;
1239
- }
1240
- if (_style.display === "none") {
1241
- return paths;
1242
1006
  }
1243
- const currentTransform = new Transform2D();
1244
- const transformStack = [];
1245
- const transform = getNodeTransform(node, currentTransform, transformStack);
1246
- if (path) {
1247
- path.applyTransform(currentTransform);
1248
- paths.push(path);
1249
- path.style = { ..._style };
1007
+ x0 = points[(length - 2) * 2];
1008
+ y0 = points[(length - 2) * 2 + 1];
1009
+ x1 = points[(length - 1) * 2];
1010
+ y1 = points[(length - 1) * 2 + 1];
1011
+ perpX = -(y0 - y1);
1012
+ perpY = x0 - x1;
1013
+ dist = Math.sqrt(perpX * perpX + perpY * perpY);
1014
+ perpX /= dist;
1015
+ perpY /= dist;
1016
+ perpX *= width;
1017
+ perpY *= width;
1018
+ verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
1019
+ verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
1020
+ if (!closedShape) {
1021
+ if (style.cap === "round") {
1022
+ indexCount += round(
1023
+ x1 - perpX * (innerWeight - outerWeight) * 0.5,
1024
+ y1 - perpY * (innerWeight - outerWeight) * 0.5,
1025
+ x1 - perpX * innerWeight,
1026
+ y1 - perpY * innerWeight,
1027
+ x1 + perpX * outerWeight,
1028
+ y1 + perpY * outerWeight,
1029
+ verts,
1030
+ false
1031
+ ) + 2;
1032
+ } else if (style.cap === "square") {
1033
+ indexCount += square(x1, y1, perpX, perpY, innerWeight, outerWeight, false, verts);
1034
+ }
1250
1035
  }
1251
- const childNodes = node.childNodes;
1252
- for (let i = 0, len = childNodes.length; i < len; i++) {
1253
- const node2 = childNodes[i];
1254
- if (isDefsNode && node2.nodeName !== "style" && node2.nodeName !== "defs")
1036
+ const eps2 = curveEps * curveEps;
1037
+ for (let i = indexStart; i < indexCount + indexStart - 2; ++i) {
1038
+ x0 = verts[i * 2];
1039
+ y0 = verts[i * 2 + 1];
1040
+ x1 = verts[(i + 1) * 2];
1041
+ y1 = verts[(i + 1) * 2 + 1];
1042
+ x2 = verts[(i + 2) * 2];
1043
+ y2 = verts[(i + 2) * 2 + 1];
1044
+ if (Math.abs(x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)) < eps2) {
1255
1045
  continue;
1256
- parseNode(node2, _style, paths, stylesheets);
1257
- }
1258
- if (transform) {
1259
- transformStack.pop();
1260
- if (transformStack.length > 0) {
1261
- currentTransform.copyFrom(transformStack[transformStack.length - 1]);
1262
- } else {
1263
- currentTransform.identity();
1264
1046
  }
1047
+ indices.push(i, i + 1, i + 2);
1265
1048
  }
1266
- return paths;
1267
- }
1268
-
1269
- function svgToPath2DSet(svg) {
1270
- const dom = svgToDom(svg);
1271
- return new Path2DSet(
1272
- parseNode(dom, {}),
1273
- dom.getAttribute("viewBox")?.trim().split(" ").map((v) => Number(v))
1274
- );
1049
+ return {
1050
+ vertices,
1051
+ indices
1052
+ };
1053
+ }
1054
+ function getOrientationOfPoints(points) {
1055
+ const m = points.length;
1056
+ if (m < 6) {
1057
+ return 1;
1058
+ }
1059
+ let area = 0;
1060
+ for (let i = 0, x1 = points[m - 2], y1 = points[m - 1]; i < m; i += 2) {
1061
+ const x2 = points[i];
1062
+ const y2 = points[i + 1];
1063
+ area += (x2 - x1) * (y2 + y1);
1064
+ x1 = x2;
1065
+ y1 = y2;
1066
+ }
1067
+ if (area < 0) {
1068
+ return -1;
1069
+ }
1070
+ return 1;
1071
+ }
1072
+ function square(x, y, nx, ny, innerWeight, outerWeight, clockwise, verts) {
1073
+ const ix = x - nx * innerWeight;
1074
+ const iy = y - ny * innerWeight;
1075
+ const ox = x + nx * outerWeight;
1076
+ const oy = y + ny * outerWeight;
1077
+ let exx;
1078
+ let eyy;
1079
+ if (clockwise) {
1080
+ exx = ny;
1081
+ eyy = -nx;
1082
+ } else {
1083
+ exx = -ny;
1084
+ eyy = nx;
1085
+ }
1086
+ const eix = ix + exx;
1087
+ const eiy = iy + eyy;
1088
+ const eox = ox + exx;
1089
+ const eoy = oy + eyy;
1090
+ verts.push(eix, eiy);
1091
+ verts.push(eox, eoy);
1092
+ return 2;
1093
+ }
1094
+ function round(cx, cy, sx, sy, ex, ey, verts, clockwise) {
1095
+ const cx2p0x = sx - cx;
1096
+ const cy2p0y = sy - cy;
1097
+ let angle0 = Math.atan2(cx2p0x, cy2p0y);
1098
+ let angle1 = Math.atan2(ex - cx, ey - cy);
1099
+ if (clockwise && angle0 < angle1) {
1100
+ angle0 += Math.PI * 2;
1101
+ } else if (!clockwise && angle0 > angle1) {
1102
+ angle1 += Math.PI * 2;
1103
+ }
1104
+ let startAngle = angle0;
1105
+ const angleDiff = angle1 - angle0;
1106
+ const absAngleDiff = Math.abs(angleDiff);
1107
+ const radius = Math.sqrt(cx2p0x * cx2p0x + cy2p0y * cy2p0y);
1108
+ const segCount = (15 * absAngleDiff * Math.sqrt(radius) / Math.PI >> 0) + 1;
1109
+ const angleInc = angleDiff / segCount;
1110
+ startAngle += angleInc;
1111
+ if (clockwise) {
1112
+ verts.push(cx, cy);
1113
+ verts.push(sx, sy);
1114
+ for (let i = 1, angle = startAngle; i < segCount; i++, angle += angleInc) {
1115
+ verts.push(cx, cy);
1116
+ verts.push(cx + Math.sin(angle) * radius, cy + Math.cos(angle) * radius);
1117
+ }
1118
+ verts.push(cx, cy);
1119
+ verts.push(ex, ey);
1120
+ } else {
1121
+ verts.push(sx, sy);
1122
+ verts.push(cx, cy);
1123
+ for (let i = 1, angle = startAngle; i < segCount; i++, angle += angleInc) {
1124
+ verts.push(cx + Math.sin(angle) * radius, cy + Math.cos(angle) * radius);
1125
+ verts.push(cx, cy);
1126
+ }
1127
+ verts.push(ex, ey);
1128
+ verts.push(cx, cy);
1129
+ }
1130
+ return segCount * 2;
1275
1131
  }
1276
-
1277
- const PI = Math.PI;
1278
- const PI_2 = PI * 2;
1279
1132
 
1280
1133
  class Transform2D {
1281
1134
  constructor(a = 1, b = 0, c = 0, d = 1, tx = 0, ty = 0) {
@@ -1558,115 +1411,78 @@ class Transform2D {
1558
1411
  equals(t2d) {
1559
1412
  return t2d.a === this.a && t2d.b === this.b && t2d.c === this.c && t2d.d === this.d && t2d.tx === this.tx && t2d.ty === this.ty;
1560
1413
  }
1561
- appendCssTransform(cssTransform) {
1562
- const transformsTexts = cssTransform.split(")");
1563
- const transform = new Transform2D();
1564
- for (let tIndex = transformsTexts.length - 1; tIndex >= 0; tIndex--) {
1565
- const transformText = transformsTexts[tIndex].trim();
1566
- if (transformText === "")
1567
- continue;
1568
- const openParPos = transformText.indexOf("(");
1569
- const closeParPos = transformText.length;
1570
- if (openParPos > 0 && openParPos < closeParPos) {
1571
- const transformType = transformText.slice(0, openParPos);
1572
- const array = parsePathDataArgs(transformText.slice(openParPos + 1));
1573
- transform.identity();
1574
- switch (transformType) {
1575
- case "translateX":
1576
- transform.translateX(array[0]);
1577
- break;
1578
- case "translateY":
1579
- transform.translateY(array[0]);
1580
- break;
1581
- case "translateZ":
1582
- transform.translateZ(array[0]);
1583
- break;
1584
- case "translate":
1585
- transform.translate(
1586
- array[0],
1587
- array[1] ?? array[0]
1588
- );
1589
- break;
1590
- case "translate3d":
1591
- transform.translate3d(
1592
- array[0],
1593
- array[1] ?? array[0],
1594
- array[2] ?? array[1] ?? array[0]
1595
- );
1596
- break;
1597
- case "scaleX":
1598
- transform.scaleX(array[0]);
1599
- break;
1600
- case "scaleY":
1601
- transform.scaleY(array[0]);
1602
- break;
1603
- case "scale":
1604
- transform.scale(
1605
- array[0],
1606
- array[1] ?? array[0]
1607
- );
1608
- break;
1609
- case "scale3d":
1610
- transform.scale3d(
1611
- array[0],
1612
- array[1] ?? array[0],
1613
- array[2] ?? array[1] ?? array[0]
1614
- );
1615
- break;
1616
- case "rotate": {
1617
- const rad = array[0] * Math.PI / 180;
1618
- if (array.length >= 3) {
1619
- const cx = array[1];
1620
- const cy = array[2];
1621
- transform.translate(-cx, -cy).rotate(rad).translate(cx, cy);
1622
- } else {
1623
- transform.rotate(rad);
1624
- }
1625
- break;
1626
- }
1627
- case "rotateX":
1628
- transform.rotateX(array[0] * Math.PI / 180);
1629
- break;
1630
- case "rotateY":
1631
- transform.rotateY(array[0] * Math.PI / 180);
1632
- break;
1633
- case "rotateZ":
1634
- transform.rotateZ(array[0] * Math.PI / 180);
1635
- break;
1636
- case "rotate3d":
1637
- transform.rotate3d(
1638
- array[0] * Math.PI / 180,
1639
- (array[1] ?? array[0]) * Math.PI / 180,
1640
- (array[2] ?? array[1] ?? array[0]) * Math.PI / 180,
1641
- (array[3] ?? array[2] ?? array[1] ?? array[0]) * Math.PI / 180
1642
- );
1643
- break;
1644
- case "skewX":
1645
- transform.set(1, 0, Math.tan(array[0] * Math.PI / 180), 1, 0, 0);
1646
- break;
1647
- case "skewY":
1648
- transform.set(1, Math.tan(array[0] * Math.PI / 180), 0, 1, 0, 0);
1649
- break;
1650
- case "skew": {
1651
- const ax = array[0];
1652
- const ay = array[1] ?? 0;
1653
- transform.set(1, Math.tan(ay * Math.PI / 180), Math.tan(ax * Math.PI / 180), 1, 0, 0);
1654
- break;
1655
- }
1656
- case "matrix":
1657
- transform.set(
1658
- array[0],
1659
- array[1],
1660
- array[2],
1661
- array[3],
1662
- array[4],
1663
- array[5]
1664
- );
1665
- break;
1666
- }
1414
+ appendCssTransform(cssTransform, ctx = {}) {
1415
+ const { width = 1, height = 1 } = ctx;
1416
+ const output = new Transform2D();
1417
+ parseCssFunctions(cssTransform, { width, height }).reverse().forEach(({ name, args }) => {
1418
+ const values = args.map((arg) => arg.normalizedIntValue);
1419
+ switch (name) {
1420
+ case "translate":
1421
+ output.translate(values[0] * width, (values[1] ?? values[0]) * height);
1422
+ break;
1423
+ case "translateX":
1424
+ output.translateX(values[0] * width);
1425
+ break;
1426
+ case "translateY":
1427
+ output.translateY(values[0] * height);
1428
+ break;
1429
+ case "translateZ":
1430
+ output.translateZ(values[0]);
1431
+ break;
1432
+ case "translate3d":
1433
+ output.translate3d(
1434
+ values[0] * width,
1435
+ (values[1] ?? values[0]) * height,
1436
+ values[2] ?? values[1] ?? values[0]
1437
+ );
1438
+ break;
1439
+ case "scale":
1440
+ output.scale(values[0], values[1] ?? values[0]);
1441
+ break;
1442
+ case "scaleX":
1443
+ output.scaleX(values[0]);
1444
+ break;
1445
+ case "scaleY":
1446
+ output.scaleY(values[0]);
1447
+ break;
1448
+ case "scale3d":
1449
+ output.scale3d(values[0], values[1] ?? values[0], values[2] ?? values[1] ?? values[0]);
1450
+ break;
1451
+ case "rotate":
1452
+ output.rotate(values[0] * PI_2);
1453
+ break;
1454
+ case "rotateX":
1455
+ output.rotateX(values[0] * PI_2);
1456
+ break;
1457
+ case "rotateY":
1458
+ output.rotateY(values[0] * PI_2);
1459
+ break;
1460
+ case "rotateZ":
1461
+ output.rotateZ(values[0] * PI_2);
1462
+ break;
1463
+ case "rotate3d":
1464
+ output.rotate3d(
1465
+ values[0] * PI_2,
1466
+ (values[1] ?? values[0]) * PI_2,
1467
+ (values[2] ?? values[1] ?? values[0]) * PI_2,
1468
+ (values[3] ?? values[2] ?? values[1] ?? values[0]) * PI_2
1469
+ );
1470
+ break;
1471
+ case "skew":
1472
+ output.skew(values[0], values[0] ?? values[1]);
1473
+ break;
1474
+ case "skewX":
1475
+ output.skewX(values[0]);
1476
+ break;
1477
+ case "skewY":
1478
+ output.skewY(values[0]);
1479
+ break;
1480
+ case "matrix":
1481
+ output.set(values[0], values[1], values[2], values[3], values[4], values[5]);
1482
+ break;
1667
1483
  }
1668
- this.prepend(transform);
1669
- }
1484
+ });
1485
+ this.prepend(output);
1670
1486
  return this;
1671
1487
  }
1672
1488
  clone() {
@@ -1708,749 +1524,976 @@ class Transform2D {
1708
1524
  }
1709
1525
  }
1710
1526
 
1711
- function catmullRom(t, p0, p1, p2, p3) {
1712
- const v0 = (p2 - p0) * 0.5;
1713
- const v1 = (p3 - p1) * 0.5;
1714
- const t2 = t * t;
1715
- const t3 = t * t2;
1716
- return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1;
1717
- }
1718
-
1719
- function cubicBezierP0(t, p) {
1720
- const k = 1 - t;
1721
- return k * k * k * p;
1722
- }
1723
- function cubicBezierP1(t, p) {
1724
- const k = 1 - t;
1725
- return 3 * k * k * t * p;
1726
- }
1727
- function cubicBezierP2(t, p) {
1728
- return 3 * (1 - t) * t * t * p;
1729
- }
1730
- function cubicBezierP3(t, p) {
1731
- return t * t * t * p;
1527
+ function svgAngle(ux, uy, vx, vy) {
1528
+ const dot = ux * vx + uy * vy;
1529
+ const len = Math.sqrt(ux * ux + uy * uy) * Math.sqrt(vx * vx + vy * vy);
1530
+ let ang = Math.acos(Math.max(-1, Math.min(1, dot / len)));
1531
+ if (ux * vy - uy * vx < 0)
1532
+ ang = -ang;
1533
+ return ang;
1732
1534
  }
1733
- function cubicBezier(t, p0, p1, p2, p3) {
1734
- return cubicBezierP0(t, p0) + cubicBezierP1(t, p1) + cubicBezierP2(t, p2) + cubicBezierP3(t, p3);
1535
+ function parseArcCommand(path, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, start, end) {
1536
+ if (rx === 0 || ry === 0) {
1537
+ path.lineTo(end.x, end.y);
1538
+ return;
1539
+ }
1540
+ xAxisRotation = xAxisRotation * Math.PI / 180;
1541
+ rx = Math.abs(rx);
1542
+ ry = Math.abs(ry);
1543
+ const dx2 = (start.x - end.x) / 2;
1544
+ const dy2 = (start.y - end.y) / 2;
1545
+ const x1p = Math.cos(xAxisRotation) * dx2 + Math.sin(xAxisRotation) * dy2;
1546
+ const y1p = -Math.sin(xAxisRotation) * dx2 + Math.cos(xAxisRotation) * dy2;
1547
+ let rxs = rx * rx;
1548
+ let rys = ry * ry;
1549
+ const x1ps = x1p * x1p;
1550
+ const y1ps = y1p * y1p;
1551
+ const cr = x1ps / rxs + y1ps / rys;
1552
+ if (cr > 1) {
1553
+ const s = Math.sqrt(cr);
1554
+ rx = s * rx;
1555
+ ry = s * ry;
1556
+ rxs = rx * rx;
1557
+ rys = ry * ry;
1558
+ }
1559
+ const dq = rxs * y1ps + rys * x1ps;
1560
+ const pq = (rxs * rys - dq) / dq;
1561
+ let q = Math.sqrt(Math.max(0, pq));
1562
+ if (largeArcFlag === sweepFlag)
1563
+ q = -q;
1564
+ const cxp = q * rx * y1p / ry;
1565
+ const cyp = -q * ry * x1p / rx;
1566
+ const cx = Math.cos(xAxisRotation) * cxp - Math.sin(xAxisRotation) * cyp + (start.x + end.x) / 2;
1567
+ const cy = Math.sin(xAxisRotation) * cxp + Math.cos(xAxisRotation) * cyp + (start.y + end.y) / 2;
1568
+ const theta = svgAngle(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);
1569
+ const delta = svgAngle((x1p - cxp) / rx, (y1p - cyp) / ry, (-x1p - cxp) / rx, (-y1p - cyp) / ry) % (Math.PI * 2);
1570
+ path.ellipse(cx, cy, rx, ry, xAxisRotation, theta, theta + delta, sweepFlag === 0);
1735
1571
  }
1736
1572
 
1737
- function fillTriangulate(pointArray, options = {}) {
1738
- let {
1739
- vertices = [],
1740
- indices = [],
1741
- holes = [],
1742
- verticesStride = 2,
1743
- verticesOffset = vertices.length / verticesStride,
1744
- indicesOffset = indices.length
1745
- } = options;
1746
- const triangles = earcut__default(pointArray, holes, 2);
1747
- if (triangles.length) {
1748
- for (let i = 0; i < triangles.length; i += 3) {
1749
- indices[indicesOffset++] = triangles[i] + verticesOffset;
1750
- indices[indicesOffset++] = triangles[i + 1] + verticesOffset;
1751
- indices[indicesOffset++] = triangles[i + 2] + verticesOffset;
1752
- }
1753
- let index = verticesOffset * verticesStride;
1754
- for (let i = 0; i < pointArray.length; i += 2) {
1755
- vertices[index] = pointArray[i];
1756
- vertices[index + 1] = pointArray[i + 1];
1757
- index += verticesStride;
1573
+ const RE$3 = {
1574
+ WHITESPACE: /[ \t\r\n]/,
1575
+ DIGIT: /\d/,
1576
+ SIGN: /[-+]/,
1577
+ POINT: /\./,
1578
+ COMMA: /,/,
1579
+ EXP: /e/i,
1580
+ FLAGS: /[01]/
1581
+ };
1582
+ function parsePathDataArgs(input, flags, stride = 0) {
1583
+ const SEP = 0;
1584
+ const INT = 1;
1585
+ const FLOAT = 2;
1586
+ const EXP = 3;
1587
+ let state = SEP;
1588
+ let seenComma = true;
1589
+ let number = "";
1590
+ let exponent = "";
1591
+ const result = [];
1592
+ function throwSyntaxError(current2, i, partial) {
1593
+ const error = new SyntaxError(`Unexpected character "${current2}" at index ${i}.`);
1594
+ error.partial = partial;
1595
+ throw error;
1596
+ }
1597
+ function newNumber() {
1598
+ if (number !== "") {
1599
+ if (exponent === "")
1600
+ result.push(Number(number));
1601
+ else result.push(Number(number) * 10 ** Number(exponent));
1758
1602
  }
1603
+ number = "";
1604
+ exponent = "";
1759
1605
  }
1760
- return {
1761
- vertices,
1762
- indices
1763
- };
1764
- }
1765
-
1766
- const RECURSION_LIMIT$1 = 8;
1767
- const FLT_EPSILON$1 = 11920929e-14;
1768
- const PATH_DISTANCE_EPSILON$1 = 1;
1769
- function getAdaptiveCubicBezierCurvePoints(sX, sY, x1, y1, x2, y2, x, y, smoothness = 0.5, points = []) {
1770
- const scale = 1;
1771
- const smoothing = Math.min(
1772
- 0.99,
1773
- // a value of 1.0 actually inverts smoothing, so we cap it at 0.99
1774
- Math.max(0, smoothness)
1775
- );
1776
- let distanceTolerance = (PATH_DISTANCE_EPSILON$1 - smoothing) / scale;
1777
- distanceTolerance *= distanceTolerance;
1778
- recursive$1(sX, sY, x1, y1, x2, y2, x, y, points, distanceTolerance, 0);
1779
- points.push(x, y);
1780
- return points;
1781
- }
1782
- function recursive$1(x1, y1, x2, y2, x3, y3, x4, y4, points, distanceTolerance, level) {
1783
- if (level > RECURSION_LIMIT$1)
1784
- return;
1785
- const x12 = (x1 + x2) / 2;
1786
- const y12 = (y1 + y2) / 2;
1787
- const x23 = (x2 + x3) / 2;
1788
- const y23 = (y2 + y3) / 2;
1789
- const x34 = (x3 + x4) / 2;
1790
- const y34 = (y3 + y4) / 2;
1791
- const x123 = (x12 + x23) / 2;
1792
- const y123 = (y12 + y23) / 2;
1793
- const x234 = (x23 + x34) / 2;
1794
- const y234 = (y23 + y34) / 2;
1795
- const x1234 = (x123 + x234) / 2;
1796
- const y1234 = (y123 + y234) / 2;
1797
- if (level > 0) {
1798
- let dx = x4 - x1;
1799
- let dy = y4 - y1;
1800
- const d2 = Math.abs((x2 - x4) * dy - (y2 - y4) * dx);
1801
- const d3 = Math.abs((x3 - x4) * dy - (y3 - y4) * dx);
1802
- if (d2 > FLT_EPSILON$1 && d3 > FLT_EPSILON$1) {
1803
- if ((d2 + d3) * (d2 + d3) <= distanceTolerance * (dx * dx + dy * dy)) {
1804
- {
1805
- points.push(x1234, y1234);
1806
- return;
1807
- }
1606
+ let current;
1607
+ const length = input.length;
1608
+ for (let i = 0; i < length; i++) {
1609
+ current = input[i];
1610
+ if (Array.isArray(flags) && flags.includes(result.length % stride) && RE$3.FLAGS.test(current)) {
1611
+ state = INT;
1612
+ number = current;
1613
+ newNumber();
1614
+ continue;
1615
+ }
1616
+ if (state === SEP) {
1617
+ if (RE$3.WHITESPACE.test(current)) {
1618
+ continue;
1808
1619
  }
1809
- } else if (d2 > FLT_EPSILON$1) {
1810
- if (d2 * d2 <= distanceTolerance * (dx * dx + dy * dy)) {
1811
- {
1812
- points.push(x1234, y1234);
1813
- return;
1814
- }
1620
+ if (RE$3.DIGIT.test(current) || RE$3.SIGN.test(current)) {
1621
+ state = INT;
1622
+ number = current;
1623
+ continue;
1815
1624
  }
1816
- } else if (d3 > FLT_EPSILON$1) {
1817
- if (d3 * d3 <= distanceTolerance * (dx * dx + dy * dy)) {
1818
- {
1819
- points.push(x1234, y1234);
1820
- return;
1625
+ if (RE$3.POINT.test(current)) {
1626
+ state = FLOAT;
1627
+ number = current;
1628
+ continue;
1629
+ }
1630
+ if (RE$3.COMMA.test(current)) {
1631
+ if (seenComma) {
1632
+ throwSyntaxError(current, i, result);
1821
1633
  }
1634
+ seenComma = true;
1822
1635
  }
1823
- } else {
1824
- dx = x1234 - (x1 + x4) / 2;
1825
- dy = y1234 - (y1 + y4) / 2;
1826
- if (dx * dx + dy * dy <= distanceTolerance) {
1827
- points.push(x1234, y1234);
1828
- return;
1636
+ }
1637
+ if (state === INT) {
1638
+ if (RE$3.DIGIT.test(current)) {
1639
+ number += current;
1640
+ continue;
1641
+ }
1642
+ if (RE$3.POINT.test(current)) {
1643
+ number += current;
1644
+ state = FLOAT;
1645
+ continue;
1646
+ }
1647
+ if (RE$3.EXP.test(current)) {
1648
+ state = EXP;
1649
+ continue;
1650
+ }
1651
+ if (RE$3.SIGN.test(current) && number.length === 1 && RE$3.SIGN.test(number[0])) {
1652
+ throwSyntaxError(current, i, result);
1829
1653
  }
1830
1654
  }
1831
- }
1832
- recursive$1(x1, y1, x12, y12, x123, y123, x1234, y1234, points, distanceTolerance, level + 1);
1833
- recursive$1(x1234, y1234, x234, y234, x34, y34, x4, y4, points, distanceTolerance, level + 1);
1834
- }
1835
-
1836
- const RECURSION_LIMIT = 8;
1837
- const FLT_EPSILON = 11920929e-14;
1838
- const PATH_DISTANCE_EPSILON = 1;
1839
- function getAdaptiveQuadraticBezierCurvePoints(sX, sY, x1, y1, x, y, smoothness = 0.5, points = []) {
1840
- const scale = 1;
1841
- const smoothing = Math.min(
1842
- 0.99,
1843
- // a value of 1.0 actually inverts smoothing, so we cap it at 0.99
1844
- Math.max(0, smoothness)
1845
- );
1846
- let distanceTolerance = (PATH_DISTANCE_EPSILON - smoothing) / scale;
1847
- distanceTolerance *= distanceTolerance;
1848
- recursive(points, sX, sY, x1, y1, x, y, distanceTolerance, 0);
1849
- points.push(x, y);
1850
- return points;
1851
- }
1852
- function recursive(points, x1, y1, x2, y2, x3, y3, distanceTolerance, level) {
1853
- if (level > RECURSION_LIMIT)
1854
- return;
1855
- const x12 = (x1 + x2) / 2;
1856
- const y12 = (y1 + y2) / 2;
1857
- const x23 = (x2 + x3) / 2;
1858
- const y23 = (y2 + y3) / 2;
1859
- const x123 = (x12 + x23) / 2;
1860
- const y123 = (y12 + y23) / 2;
1861
- let dx = x3 - x1;
1862
- let dy = y3 - y1;
1863
- const d = Math.abs((x2 - x3) * dy - (y2 - y3) * dx);
1864
- if (d > FLT_EPSILON) {
1865
- if (d * d <= distanceTolerance * (dx * dx + dy * dy)) {
1866
- {
1867
- points.push(x123, y123);
1868
- return;
1655
+ if (state === FLOAT) {
1656
+ if (RE$3.DIGIT.test(current)) {
1657
+ number += current;
1658
+ continue;
1659
+ }
1660
+ if (RE$3.EXP.test(current)) {
1661
+ state = EXP;
1662
+ continue;
1663
+ }
1664
+ if (RE$3.POINT.test(current) && number[number.length - 1] === ".") {
1665
+ throwSyntaxError(current, i, result);
1869
1666
  }
1870
1667
  }
1871
- } else {
1872
- dx = x123 - (x1 + x3) / 2;
1873
- dy = y123 - (y1 + y3) / 2;
1874
- if (dx * dx + dy * dy <= distanceTolerance) {
1875
- points.push(x123, y123);
1876
- return;
1668
+ if (state === EXP) {
1669
+ if (RE$3.DIGIT.test(current)) {
1670
+ exponent += current;
1671
+ continue;
1672
+ }
1673
+ if (RE$3.SIGN.test(current)) {
1674
+ if (exponent === "") {
1675
+ exponent += current;
1676
+ continue;
1677
+ }
1678
+ if (exponent.length === 1 && RE$3.SIGN.test(exponent)) {
1679
+ throwSyntaxError(current, i, result);
1680
+ }
1681
+ }
1877
1682
  }
1878
- }
1879
- recursive(points, x1, y1, x12, y12, x123, y123, distanceTolerance, level + 1);
1880
- recursive(points, x123, y123, x23, y23, x3, y3, distanceTolerance, level + 1);
1881
- }
1882
-
1883
- function getDirectedArea(vertices) {
1884
- let area = 0;
1885
- const n = vertices.length;
1886
- for (let i = 0; i < n; i += 2) {
1887
- const x0 = vertices[i];
1888
- const y0 = vertices[i + 1];
1889
- const x1 = vertices[(i + 2) % (n - 1)];
1890
- const y1 = vertices[(i + 3) % n];
1891
- area += x0 * y1 - x1 * y0;
1892
- }
1893
- return area / 2;
1894
- }
1895
-
1896
- function toKebabCase(str) {
1897
- return str.replace(/[^a-z0-9]/gi, "-").replace(/\B([A-Z])/g, "-$1").toLowerCase();
1898
- }
1899
- function getIntersectionPoint(p1, p2, q1, q2) {
1900
- const r = p2.clone().sub(p1);
1901
- const s = q2.clone().sub(q1);
1902
- const q1p1 = q1.clone().sub(p1);
1903
- const crossRS = r.cross(s);
1904
- if (crossRS === 0) {
1905
- return new Vector2(
1906
- (p1.x + q1.x) / 2,
1907
- (p1.y + q1.y) / 2
1908
- );
1909
- }
1910
- const t = q1p1.cross(s) / crossRS;
1911
- if (Math.abs(t) > 1) {
1912
- return new Vector2(
1913
- (p1.x + q1.x) / 2,
1914
- (p1.y + q1.y) / 2
1915
- );
1916
- }
1917
- return new Vector2(
1918
- p1.x + t * r.x,
1919
- p1.y + t * r.y
1920
- );
1921
- }
1922
-
1923
- function cross(ax, ay, bx, by, cx, cy) {
1924
- return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
1925
- }
1926
- function windingNumber(px, py, polygon) {
1927
- const polygonLen = polygon.length;
1928
- let wn = 0;
1929
- for (let i = 0, j = polygonLen - 2; i < polygonLen; j = i, i += 2) {
1930
- const xi = polygon[i];
1931
- const yi = polygon[i + 1];
1932
- const xj = polygon[j];
1933
- const yj = polygon[j + 1];
1934
- if (yi <= py) {
1935
- if (yj > py && cross(xj, yj, xi, yi, px, py) > 0)
1936
- wn++;
1683
+ if (RE$3.WHITESPACE.test(current)) {
1684
+ newNumber();
1685
+ state = SEP;
1686
+ seenComma = false;
1687
+ } else if (RE$3.COMMA.test(current)) {
1688
+ newNumber();
1689
+ state = SEP;
1690
+ seenComma = true;
1691
+ } else if (RE$3.SIGN.test(current)) {
1692
+ newNumber();
1693
+ state = INT;
1694
+ number = current;
1695
+ } else if (RE$3.POINT.test(current)) {
1696
+ newNumber();
1697
+ state = FLOAT;
1698
+ number = current;
1937
1699
  } else {
1938
- if (yj <= py && cross(xj, yj, xi, yi, px, py) < 0)
1939
- wn--;
1700
+ throwSyntaxError(current, i, result);
1940
1701
  }
1941
1702
  }
1942
- return wn;
1703
+ newNumber();
1704
+ return result;
1943
1705
  }
1944
- function distance(p1, p2) {
1945
- const dx = p2[0] - p1[0];
1946
- const dy = p2[1] - p1[1];
1947
- return Math.sqrt(dx * dx + dy * dy);
1706
+
1707
+ function getReflection(a, b) {
1708
+ return a - (b - a);
1948
1709
  }
1949
- function nonzeroFillRule(paths) {
1950
- const results = paths.map((_, i) => ({ index: i }));
1951
- const testPointsGroups = paths.map((path) => {
1952
- const len = path.length;
1953
- if (!len) {
1954
- return [];
1955
- }
1956
- let xMinYAuto = [Number.MAX_SAFE_INTEGER, 0];
1957
- let xAutoYMin = [0, Number.MAX_SAFE_INTEGER];
1958
- let xMaxYAuto = [Number.MIN_SAFE_INTEGER, 0];
1959
- let xAutoYMax = [0, Number.MIN_SAFE_INTEGER];
1960
- for (let i = 0; i < len; i += 2) {
1961
- const x = path[i];
1962
- const y = path[i + 1];
1963
- if (xMinYAuto[0] > x) {
1964
- xMinYAuto = [x, y];
1710
+ function svgPathCommandsAddToPath2D(commands, path) {
1711
+ const current = new Vector2();
1712
+ const control = new Vector2();
1713
+ for (let i = 0, l = commands.length; i < l; i++) {
1714
+ const cmd = commands[i];
1715
+ if (cmd.type === "m" || cmd.type === "M") {
1716
+ if (cmd.type === "m") {
1717
+ current.add(cmd);
1718
+ } else {
1719
+ current.copyFrom(cmd);
1965
1720
  }
1966
- if (xAutoYMin[1] > y) {
1967
- xAutoYMin = [x, y];
1721
+ path.moveTo(current.x, current.y);
1722
+ control.copyFrom(current);
1723
+ } else if (cmd.type === "h" || cmd.type === "H") {
1724
+ if (cmd.type === "h") {
1725
+ current.x += cmd.x;
1726
+ } else {
1727
+ current.x = cmd.x;
1968
1728
  }
1969
- if (xMaxYAuto[0] < x) {
1970
- xMaxYAuto = [x, y];
1729
+ path.lineTo(current.x, current.y);
1730
+ control.copyFrom(current);
1731
+ } else if (cmd.type === "v" || cmd.type === "V") {
1732
+ if (cmd.type === "v") {
1733
+ current.y += cmd.y;
1734
+ } else {
1735
+ current.y = cmd.y;
1971
1736
  }
1972
- if (xAutoYMax[1] < y) {
1973
- xAutoYMax = [x, y];
1737
+ path.lineTo(current.x, current.y);
1738
+ control.copyFrom(current);
1739
+ } else if (cmd.type === "l" || cmd.type === "L") {
1740
+ if (cmd.type === "l") {
1741
+ current.add(cmd);
1742
+ } else {
1743
+ current.copyFrom(cmd);
1974
1744
  }
1975
- }
1976
- const mid = [
1977
- (xMinYAuto[0] + xMaxYAuto[0]) / 2,
1978
- (xAutoYMin[1] + xAutoYMax[1]) / 2
1979
- ];
1980
- let xMidYMinDx;
1981
- let xMidYMaxDx;
1982
- let xMidYMin;
1983
- let xMidYMax;
1984
- let xMinYMidDy;
1985
- let xMaxYMidDy;
1986
- let xMinYMid;
1987
- let xMaxYMid;
1988
- for (let i = 0; i < len; i += 2) {
1989
- const x = path[i];
1990
- const y = path[i + 1];
1991
- const _dx = Math.abs(x - mid[0]);
1992
- const _dy = Math.abs(y - mid[1]);
1993
- if (y < mid[1] && (!xMidYMinDx || _dx < xMidYMinDx)) {
1994
- xMidYMinDx = _dx;
1995
- xMidYMin = [x, y];
1745
+ path.lineTo(current.x, current.y);
1746
+ control.copyFrom(current);
1747
+ } else if (cmd.type === "c" || cmd.type === "C") {
1748
+ if (cmd.type === "c") {
1749
+ path.bezierCurveTo(
1750
+ current.x + cmd.x1,
1751
+ current.y + cmd.y1,
1752
+ current.x + cmd.x2,
1753
+ current.y + cmd.y2,
1754
+ current.x + cmd.x,
1755
+ current.y + cmd.y
1756
+ );
1757
+ control.x = current.x + cmd.x2;
1758
+ control.y = current.y + cmd.y2;
1759
+ current.add(cmd);
1760
+ } else {
1761
+ path.bezierCurveTo(
1762
+ cmd.x1,
1763
+ cmd.y1,
1764
+ cmd.x2,
1765
+ cmd.y2,
1766
+ cmd.x,
1767
+ cmd.y
1768
+ );
1769
+ control.x = cmd.x2;
1770
+ control.y = cmd.y2;
1771
+ current.copyFrom(cmd);
1996
1772
  }
1997
- if (y > mid[1] && (!xMidYMaxDx || _dx < xMidYMaxDx)) {
1998
- xMidYMaxDx = _dx;
1999
- xMidYMax = [x, y];
1773
+ } else if (cmd.type === "s" || cmd.type === "S") {
1774
+ if (cmd.type === "s") {
1775
+ path.bezierCurveTo(
1776
+ getReflection(current.x, control.x),
1777
+ getReflection(current.y, control.y),
1778
+ current.x + cmd.x2,
1779
+ current.y + cmd.y2,
1780
+ current.x + cmd.x,
1781
+ current.y + cmd.y
1782
+ );
1783
+ control.x = current.x + cmd.x2;
1784
+ control.y = current.y + cmd.y2;
1785
+ current.add(cmd);
1786
+ } else {
1787
+ path.bezierCurveTo(
1788
+ getReflection(current.x, control.x),
1789
+ getReflection(current.y, control.y),
1790
+ cmd.x2,
1791
+ cmd.y2,
1792
+ cmd.x,
1793
+ cmd.y
1794
+ );
1795
+ control.x = cmd.x2;
1796
+ control.y = cmd.y2;
1797
+ current.copyFrom(cmd);
2000
1798
  }
2001
- if (x < mid[0] && (!xMinYMidDy || _dy < xMinYMidDy)) {
2002
- xMinYMidDy = _dy;
2003
- xMinYMid = [x, y];
1799
+ } else if (cmd.type === "q" || cmd.type === "Q") {
1800
+ if (cmd.type === "q") {
1801
+ path.quadraticCurveTo(
1802
+ current.x + cmd.x1,
1803
+ current.y + cmd.y1,
1804
+ current.x + cmd.x,
1805
+ current.y + cmd.y
1806
+ );
1807
+ control.x = current.x + cmd.x1;
1808
+ control.y = current.y + cmd.y1;
1809
+ current.add(cmd);
1810
+ } else {
1811
+ path.quadraticCurveTo(
1812
+ cmd.x1,
1813
+ cmd.y1,
1814
+ cmd.x,
1815
+ cmd.y
1816
+ );
1817
+ control.x = cmd.x1;
1818
+ control.y = cmd.y1;
1819
+ current.copyFrom(cmd);
2004
1820
  }
2005
- if (x > mid[0] && (!xMaxYMidDy || _dy < xMaxYMidDy)) {
2006
- xMaxYMidDy = _dy;
2007
- xMaxYMid = [x, y];
1821
+ } else if (cmd.type === "t" || cmd.type === "T") {
1822
+ const rx = getReflection(current.x, control.x);
1823
+ const ry = getReflection(current.y, control.y);
1824
+ control.x = rx;
1825
+ control.y = ry;
1826
+ if (cmd.type === "t") {
1827
+ path.quadraticCurveTo(
1828
+ rx,
1829
+ ry,
1830
+ current.x + cmd.x,
1831
+ current.y + cmd.y
1832
+ );
1833
+ current.add(cmd);
1834
+ } else {
1835
+ path.quadraticCurveTo(
1836
+ rx,
1837
+ ry,
1838
+ cmd.x,
1839
+ cmd.y
1840
+ );
1841
+ current.copyFrom(cmd);
2008
1842
  }
2009
- }
2010
- return [
2011
- xMinYAuto,
2012
- xAutoYMin,
2013
- xMaxYAuto,
2014
- xAutoYMax,
2015
- xMidYMin,
2016
- xMidYMax,
2017
- xMinYMid,
2018
- xMaxYMid
2019
- ].filter(Boolean);
2020
- });
2021
- for (let i = 0, len = paths.length; i < len; i++) {
2022
- const _results = [];
2023
- const testPoints = testPointsGroups[i];
2024
- for (let j = 0; j < len; j++) {
2025
- if (i === j)
2026
- continue;
2027
- const wnMap = {};
2028
- const wnList = [];
2029
- for (let p = 0, pLen = testPoints.length; p < pLen; p++) {
2030
- const [x, y] = testPoints[p];
2031
- const winding = windingNumber(x, y, paths[j]);
2032
- wnMap[winding] = (wnMap[winding] ?? 0) + 1;
2033
- wnList.push(winding);
1843
+ } else if (cmd.type === "a" || cmd.type === "A") {
1844
+ const start = current.clone();
1845
+ if (cmd.type === "a") {
1846
+ if (cmd.x === 0 && cmd.y === 0)
1847
+ continue;
1848
+ current.add(cmd);
1849
+ } else {
1850
+ if (current.equals(cmd))
1851
+ continue;
1852
+ current.copyFrom(cmd);
2034
1853
  }
2035
- if (wnList.filter((v) => v !== 0).length > wnList.filter((v) => v === 0).length) {
2036
- _results.push({
2037
- index: i,
2038
- parentIndex: j,
2039
- winding: Number(
2040
- Array.from(Object.entries(wnMap)).sort((a, b) => b[1] - a[1])?.[0]?.[0] ?? 0
2041
- ),
2042
- dist: distance(testPointsGroups[i][0], testPointsGroups[j][0])
2043
- });
1854
+ control.copyFrom(current);
1855
+ parseArcCommand(
1856
+ path,
1857
+ cmd.rx,
1858
+ cmd.ry,
1859
+ cmd.angle,
1860
+ cmd.largeArcFlag,
1861
+ cmd.sweepFlag,
1862
+ start,
1863
+ current
1864
+ );
1865
+ } else if (cmd.type === "z" || cmd.type === "Z") {
1866
+ if (path.startPoint) {
1867
+ current.copyFrom(path.startPoint);
2044
1868
  }
2045
- }
2046
- if (_results.reduce((total, item) => total + item.winding, 0) !== 0) {
2047
- _results.sort((a, b) => a.dist - b.dist);
2048
- results[i] = _results[0];
1869
+ path.closePath();
1870
+ } else {
1871
+ console.warn("Unsupported commands", cmd);
2049
1872
  }
2050
1873
  }
2051
- return results;
2052
- }
2053
-
2054
- function quadraticBezierP0(t, p) {
2055
- const k = 1 - t;
2056
- return k * k * p;
2057
- }
2058
- function quadraticBezierP1(t, p) {
2059
- return 2 * (1 - t) * t * p;
2060
- }
2061
- function quadraticBezierP2(t, p) {
2062
- return t * t * p;
2063
- }
2064
- function quadraticBezier(t, p0, p1, p2) {
2065
- return quadraticBezierP0(t, p0) + quadraticBezierP1(t, p1) + quadraticBezierP2(t, p2);
2066
1874
  }
2067
1875
 
2068
- const closePointEps = 1e-4;
2069
- const curveEps = 1e-4;
2070
- function strokeTriangulate(points, options = {}) {
2071
- const {
2072
- vertices = [],
2073
- indices = [],
2074
- lineStyle = {
2075
- alignment: 0.5,
2076
- cap: "butt",
2077
- join: "miter",
2078
- width: 1,
2079
- miterLimit: 10
2080
- },
2081
- flipAlignment = false,
2082
- closed = true
2083
- } = options;
2084
- const eps = closePointEps;
2085
- if (points.length === 0) {
2086
- return { vertices, indices };
2087
- }
2088
- const style = lineStyle;
2089
- let alignment = style.alignment;
2090
- if (lineStyle.alignment !== 0.5) {
2091
- let orientation = getOrientationOfPoints(points);
2092
- if (flipAlignment)
2093
- orientation *= -1;
2094
- alignment = (alignment - 0.5) * orientation + 0.5;
2095
- }
2096
- const firstPoint = { x: points[0], y: points[1] };
2097
- const lastPoint = { x: points[points.length - 2], y: points[points.length - 1] };
2098
- const closedShape = closed;
2099
- const closedPath = Math.abs(firstPoint.x - lastPoint.x) < eps && Math.abs(firstPoint.y - lastPoint.y) < eps;
2100
- if (closedShape) {
2101
- points = points.slice();
2102
- if (closedPath) {
2103
- points.pop();
2104
- points.pop();
2105
- lastPoint.x = points[points.length - 2];
2106
- lastPoint.y = points[points.length - 1];
1876
+ function svgPathCommandsToData(commands) {
1877
+ let first;
1878
+ let prev;
1879
+ const data = [];
1880
+ for (let i = 0, len = commands.length; i < len; i++) {
1881
+ const cmd = commands[i];
1882
+ switch (cmd.type) {
1883
+ case "m":
1884
+ case "M":
1885
+ if (cmd.x.toFixed(4) === prev?.x.toFixed(4) && cmd.y.toFixed(4) === prev?.y.toFixed(4)) {
1886
+ continue;
1887
+ }
1888
+ data.push(`${cmd.type} ${cmd.x} ${cmd.y}`);
1889
+ prev = { x: cmd.x, y: cmd.y };
1890
+ first = { x: cmd.x, y: cmd.y };
1891
+ break;
1892
+ case "h":
1893
+ case "H":
1894
+ data.push(`${cmd.type} ${cmd.x}`);
1895
+ prev = { x: cmd.x, y: prev?.y ?? 0 };
1896
+ break;
1897
+ case "v":
1898
+ case "V":
1899
+ data.push(`${cmd.type} ${cmd.y}`);
1900
+ prev = { x: prev?.x ?? 0, y: cmd.y };
1901
+ break;
1902
+ case "l":
1903
+ case "L":
1904
+ data.push(`${cmd.type} ${cmd.x} ${cmd.y}`);
1905
+ prev = { x: cmd.x, y: cmd.y };
1906
+ break;
1907
+ case "c":
1908
+ case "C":
1909
+ data.push(`${cmd.type} ${cmd.x1} ${cmd.y1} ${cmd.x2} ${cmd.y2} ${cmd.x} ${cmd.y}`);
1910
+ prev = { x: cmd.x, y: cmd.y };
1911
+ break;
1912
+ case "s":
1913
+ case "S":
1914
+ data.push(`${cmd.type} ${cmd.x2} ${cmd.y2} ${cmd.x} ${cmd.y}`);
1915
+ prev = { x: cmd.x, y: cmd.y };
1916
+ break;
1917
+ case "q":
1918
+ case "Q":
1919
+ data.push(`${cmd.type} ${cmd.x1} ${cmd.y1} ${cmd.x} ${cmd.y}`);
1920
+ prev = { x: cmd.x, y: cmd.y };
1921
+ break;
1922
+ case "t":
1923
+ case "T":
1924
+ data.push(`${cmd.type} ${cmd.x} ${cmd.y}`);
1925
+ prev = { x: cmd.x, y: cmd.y };
1926
+ break;
1927
+ case "a":
1928
+ case "A":
1929
+ data.push(`${cmd.type} ${cmd.rx} ${cmd.ry} ${cmd.angle} ${cmd.largeArcFlag} ${cmd.sweepFlag} ${cmd.x} ${cmd.y}`);
1930
+ prev = { x: cmd.x, y: cmd.y };
1931
+ break;
1932
+ case "z":
1933
+ case "Z":
1934
+ data.push(cmd.type);
1935
+ if (first) {
1936
+ prev = { x: first.x, y: first.y };
1937
+ }
1938
+ break;
2107
1939
  }
2108
- const midPointX = (firstPoint.x + lastPoint.x) * 0.5;
2109
- const midPointY = (lastPoint.y + firstPoint.y) * 0.5;
2110
- points.unshift(midPointX, midPointY);
2111
- points.push(midPointX, midPointY);
2112
1940
  }
2113
- const verts = vertices;
2114
- const length = points.length / 2;
2115
- let indexCount = points.length;
2116
- const indexStart = verts.length / 2;
2117
- const width = style.width / 2;
2118
- const widthSquared = width * width;
2119
- const miterLimitSquared = style.miterLimit * style.miterLimit;
2120
- let x0 = points[0];
2121
- let y0 = points[1];
2122
- let x1 = points[2];
2123
- let y1 = points[3];
2124
- let x2 = 0;
2125
- let y2 = 0;
2126
- let perpX = -(y0 - y1);
2127
- let perpY = x0 - x1;
2128
- let perp1x = 0;
2129
- let perp1y = 0;
2130
- let dist = Math.sqrt(perpX * perpX + perpY * perpY);
2131
- perpX /= dist;
2132
- perpY /= dist;
2133
- perpX *= width;
2134
- perpY *= width;
2135
- const ratio = alignment;
2136
- const innerWeight = (1 - ratio) * 2;
2137
- const outerWeight = ratio * 2;
2138
- if (!closedShape) {
2139
- if (style.cap === "round") {
2140
- indexCount += round(
2141
- x0 - perpX * (innerWeight - outerWeight) * 0.5,
2142
- y0 - perpY * (innerWeight - outerWeight) * 0.5,
2143
- x0 - perpX * innerWeight,
2144
- y0 - perpY * innerWeight,
2145
- x0 + perpX * outerWeight,
2146
- y0 + perpY * outerWeight,
2147
- verts,
2148
- true
2149
- ) + 2;
2150
- } else if (style.cap === "square") {
2151
- indexCount += square(x0, y0, perpX, perpY, innerWeight, outerWeight, true, verts);
2152
- }
1941
+ return data.join(" ");
1942
+ }
1943
+
1944
+ const RE$2 = /[a-df-z][^a-df-z]*/gi;
1945
+ function svgPathDataToCommands(data) {
1946
+ const commands = [];
1947
+ const matched = data.match(RE$2);
1948
+ if (!matched) {
1949
+ return commands;
2153
1950
  }
2154
- verts.push(
2155
- x0 - perpX * innerWeight,
2156
- y0 - perpY * innerWeight
2157
- );
2158
- verts.push(
2159
- x0 + perpX * outerWeight,
2160
- y0 + perpY * outerWeight
2161
- );
2162
- for (let i = 1; i < length - 1; ++i) {
2163
- x0 = points[(i - 1) * 2];
2164
- y0 = points[(i - 1) * 2 + 1];
2165
- x1 = points[i * 2];
2166
- y1 = points[i * 2 + 1];
2167
- x2 = points[(i + 1) * 2];
2168
- y2 = points[(i + 1) * 2 + 1];
2169
- perpX = -(y0 - y1);
2170
- perpY = x0 - x1;
2171
- dist = Math.sqrt(perpX * perpX + perpY * perpY);
2172
- perpX /= dist;
2173
- perpY /= dist;
2174
- perpX *= width;
2175
- perpY *= width;
2176
- perp1x = -(y1 - y2);
2177
- perp1y = x1 - x2;
2178
- dist = Math.sqrt(perp1x * perp1x + perp1y * perp1y);
2179
- perp1x /= dist;
2180
- perp1y /= dist;
2181
- perp1x *= width;
2182
- perp1y *= width;
2183
- const dx0 = x1 - x0;
2184
- const dy0 = y0 - y1;
2185
- const dx1 = x1 - x2;
2186
- const dy1 = y2 - y1;
2187
- const dot = dx0 * dx1 + dy0 * dy1;
2188
- const cross = dy0 * dx1 - dy1 * dx0;
2189
- const clockwise = cross < 0;
2190
- if (Math.abs(cross) < 1e-3 * Math.abs(dot)) {
2191
- verts.push(
2192
- x1 - perpX * innerWeight,
2193
- y1 - perpY * innerWeight
2194
- );
2195
- verts.push(
2196
- x1 + perpX * outerWeight,
2197
- y1 + perpY * outerWeight
2198
- );
2199
- if (dot >= 0) {
2200
- if (style.join === "round") {
2201
- indexCount += round(
2202
- x1,
2203
- y1,
2204
- x1 - perpX * innerWeight,
2205
- y1 - perpY * innerWeight,
2206
- x1 - perp1x * innerWeight,
2207
- y1 - perp1y * innerWeight,
2208
- verts,
2209
- false
2210
- ) + 4;
2211
- } else {
2212
- indexCount += 2;
1951
+ for (let i = 0, len = matched.length; i < len; i++) {
1952
+ const command = matched[i];
1953
+ const type = command.charAt(0);
1954
+ const data2 = command.slice(1).trim();
1955
+ let args;
1956
+ switch (type) {
1957
+ case "m":
1958
+ case "M":
1959
+ args = parsePathDataArgs(data2);
1960
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 2) {
1961
+ if (i2 === 0) {
1962
+ commands.push({ type, x: args[i2], y: args[i2 + 1] });
1963
+ } else {
1964
+ commands.push({ type: type === "m" ? "l" : "L", x: args[i2], y: args[i2 + 1] });
1965
+ }
2213
1966
  }
2214
- verts.push(
2215
- x1 - perp1x * outerWeight,
2216
- y1 - perp1y * outerWeight
2217
- );
2218
- verts.push(
2219
- x1 + perp1x * innerWeight,
2220
- y1 + perp1y * innerWeight
2221
- );
2222
- }
2223
- continue;
2224
- }
2225
- const c1 = (-perpX + x0) * (-perpY + y1) - (-perpX + x1) * (-perpY + y0);
2226
- const c2 = (-perp1x + x2) * (-perp1y + y1) - (-perp1x + x1) * (-perp1y + y2);
2227
- const px = (dx0 * c2 - dx1 * c1) / cross;
2228
- const py = (dy1 * c1 - dy0 * c2) / cross;
2229
- const pDist = (px - x1) * (px - x1) + (py - y1) * (py - y1);
2230
- const imx = x1 + (px - x1) * innerWeight;
2231
- const imy = y1 + (py - y1) * innerWeight;
2232
- const omx = x1 - (px - x1) * outerWeight;
2233
- const omy = y1 - (py - y1) * outerWeight;
2234
- const smallerInsideSegmentSq = Math.min(dx0 * dx0 + dy0 * dy0, dx1 * dx1 + dy1 * dy1);
2235
- const insideWeight = clockwise ? innerWeight : outerWeight;
2236
- const smallerInsideDiagonalSq = smallerInsideSegmentSq + insideWeight * insideWeight * widthSquared;
2237
- const insideMiterOk = pDist <= smallerInsideDiagonalSq;
2238
- if (insideMiterOk) {
2239
- if (style.join === "bevel" || pDist / widthSquared > miterLimitSquared) {
2240
- if (clockwise) {
2241
- verts.push(imx, imy);
2242
- verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
2243
- verts.push(imx, imy);
2244
- verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
2245
- } else {
2246
- verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
2247
- verts.push(omx, omy);
2248
- verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
2249
- verts.push(omx, omy);
1967
+ break;
1968
+ case "h":
1969
+ case "H":
1970
+ args = parsePathDataArgs(data2);
1971
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2++) {
1972
+ commands.push({ type, x: args[i2] });
2250
1973
  }
2251
- indexCount += 2;
2252
- } else if (style.join === "round") {
2253
- if (clockwise) {
2254
- verts.push(imx, imy);
2255
- verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
2256
- indexCount += round(
2257
- x1,
2258
- y1,
2259
- x1 + perpX * outerWeight,
2260
- y1 + perpY * outerWeight,
2261
- x1 + perp1x * outerWeight,
2262
- y1 + perp1y * outerWeight,
2263
- verts,
2264
- true
2265
- ) + 4;
2266
- verts.push(imx, imy);
2267
- verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
2268
- } else {
2269
- verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
2270
- verts.push(omx, omy);
2271
- indexCount += round(
2272
- x1,
2273
- y1,
2274
- x1 - perpX * innerWeight,
2275
- y1 - perpY * innerWeight,
2276
- x1 - perp1x * innerWeight,
2277
- y1 - perp1y * innerWeight,
2278
- verts,
2279
- false
2280
- ) + 4;
2281
- verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
2282
- verts.push(omx, omy);
1974
+ break;
1975
+ case "v":
1976
+ case "V":
1977
+ args = parsePathDataArgs(data2);
1978
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2++) {
1979
+ commands.push({ type, y: args[i2] });
2283
1980
  }
2284
- } else {
2285
- verts.push(imx, imy);
2286
- verts.push(omx, omy);
2287
- }
2288
- } else {
2289
- verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
2290
- verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
2291
- if (style.join === "round") {
2292
- if (clockwise) {
2293
- indexCount += round(
2294
- x1,
2295
- y1,
2296
- x1 + perpX * outerWeight,
2297
- y1 + perpY * outerWeight,
2298
- x1 + perp1x * outerWeight,
2299
- y1 + perp1y * outerWeight,
2300
- verts,
2301
- true
2302
- ) + 2;
2303
- } else {
2304
- indexCount += round(
2305
- x1,
2306
- y1,
2307
- x1 - perpX * innerWeight,
2308
- y1 - perpY * innerWeight,
2309
- x1 - perp1x * innerWeight,
2310
- y1 - perp1y * innerWeight,
2311
- verts,
2312
- false
2313
- ) + 2;
1981
+ break;
1982
+ case "l":
1983
+ case "L":
1984
+ args = parsePathDataArgs(data2);
1985
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 2) {
1986
+ commands.push({ type, x: args[i2], y: args[i2 + 1] });
2314
1987
  }
2315
- } else if (style.join === "miter" && pDist / widthSquared <= miterLimitSquared) {
2316
- if (clockwise) {
2317
- verts.push(omx, omy);
2318
- verts.push(omx, omy);
2319
- } else {
2320
- verts.push(imx, imy);
2321
- verts.push(imx, imy);
1988
+ break;
1989
+ case "c":
1990
+ case "C":
1991
+ args = parsePathDataArgs(data2);
1992
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 6) {
1993
+ commands.push({
1994
+ type,
1995
+ x1: args[i2],
1996
+ y1: args[i2 + 1],
1997
+ x2: args[i2 + 2],
1998
+ y2: args[i2 + 3],
1999
+ x: args[i2 + 4],
2000
+ y: args[i2 + 5]
2001
+ });
2322
2002
  }
2323
- indexCount += 2;
2003
+ break;
2004
+ case "s":
2005
+ case "S":
2006
+ args = parsePathDataArgs(data2);
2007
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 4) {
2008
+ commands.push({
2009
+ type,
2010
+ x2: args[i2],
2011
+ y2: args[i2 + 1],
2012
+ x: args[i2 + 2],
2013
+ y: args[i2 + 3]
2014
+ });
2015
+ }
2016
+ break;
2017
+ case "q":
2018
+ case "Q":
2019
+ args = parsePathDataArgs(data2);
2020
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 4) {
2021
+ commands.push({
2022
+ type,
2023
+ x1: args[i2],
2024
+ y1: args[i2 + 1],
2025
+ x: args[i2 + 2],
2026
+ y: args[i2 + 3]
2027
+ });
2028
+ }
2029
+ break;
2030
+ case "t":
2031
+ case "T":
2032
+ args = parsePathDataArgs(data2);
2033
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 2) {
2034
+ commands.push({
2035
+ type,
2036
+ x: args[i2],
2037
+ y: args[i2 + 1]
2038
+ });
2039
+ }
2040
+ break;
2041
+ case "a":
2042
+ case "A":
2043
+ args = parsePathDataArgs(data2, [3, 4], 7);
2044
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 7) {
2045
+ commands.push({
2046
+ type,
2047
+ rx: args[i2],
2048
+ ry: args[i2 + 1],
2049
+ angle: args[i2 + 2],
2050
+ largeArcFlag: args[i2 + 3],
2051
+ sweepFlag: args[i2 + 4],
2052
+ x: args[i2 + 5],
2053
+ y: args[i2 + 6]
2054
+ });
2055
+ }
2056
+ break;
2057
+ case "z":
2058
+ case "Z":
2059
+ commands.push({
2060
+ type
2061
+ });
2062
+ break;
2063
+ default:
2064
+ console.warn(command);
2065
+ }
2066
+ }
2067
+ return commands;
2068
+ }
2069
+
2070
+ const dataUri = "data:image/svg+xml;";
2071
+ const base64DataUri = `${dataUri}base64,`;
2072
+ const utf8DataUri = `${dataUri}charset=utf8,`;
2073
+ function svgToDom(svg) {
2074
+ if (typeof svg === "string") {
2075
+ let xml;
2076
+ if (svg.startsWith(base64DataUri)) {
2077
+ svg = svg.substring(base64DataUri.length, svg.length);
2078
+ xml = atob(svg);
2079
+ } else if (svg.startsWith(utf8DataUri)) {
2080
+ svg = svg.substring(utf8DataUri.length, svg.length);
2081
+ xml = decodeURIComponent(svg);
2082
+ } else {
2083
+ xml = svg;
2084
+ }
2085
+ const doc = new DOMParser().parseFromString(xml, "text/xml");
2086
+ const error = doc.querySelector("parsererror");
2087
+ if (error) {
2088
+ throw new Error(`${error.textContent ?? "parser error"}
2089
+ ${xml}`);
2090
+ }
2091
+ return doc.documentElement;
2092
+ } else {
2093
+ return svg;
2094
+ }
2095
+ }
2096
+
2097
+ const defaultUnit = "px";
2098
+ const defaultDPI = 90;
2099
+ const units = ["mm", "cm", "in", "pt", "pc", "px"];
2100
+ const unitConversion = {
2101
+ mm: {
2102
+ mm: 1,
2103
+ cm: 0.1,
2104
+ in: 1 / 25.4,
2105
+ pt: 72 / 25.4,
2106
+ pc: 6 / 25.4,
2107
+ px: -1
2108
+ },
2109
+ cm: {
2110
+ mm: 10,
2111
+ cm: 1,
2112
+ in: 1 / 2.54,
2113
+ pt: 72 / 2.54,
2114
+ pc: 6 / 2.54,
2115
+ px: -1
2116
+ },
2117
+ in: {
2118
+ mm: 25.4,
2119
+ cm: 2.54,
2120
+ in: 1,
2121
+ pt: 72,
2122
+ pc: 6,
2123
+ px: -1
2124
+ },
2125
+ pt: {
2126
+ mm: 25.4 / 72,
2127
+ cm: 2.54 / 72,
2128
+ in: 1 / 72,
2129
+ pt: 1,
2130
+ pc: 6 / 72,
2131
+ px: -1
2132
+ },
2133
+ pc: {
2134
+ mm: 25.4 / 6,
2135
+ cm: 2.54 / 6,
2136
+ in: 1 / 6,
2137
+ pt: 72 / 6,
2138
+ pc: 1,
2139
+ px: -1
2140
+ },
2141
+ px: {
2142
+ px: 1
2143
+ }
2144
+ };
2145
+ function parseFloatWithUnits(string) {
2146
+ let theUnit = "px";
2147
+ if (typeof string === "string") {
2148
+ for (let i = 0, n = units.length; i < n; i++) {
2149
+ const u = units[i];
2150
+ if (string.endsWith(u)) {
2151
+ theUnit = u;
2152
+ string = string.substring(0, string.length - u.length);
2153
+ break;
2324
2154
  }
2325
- verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
2326
- verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
2327
- indexCount += 2;
2328
2155
  }
2329
2156
  }
2330
- x0 = points[(length - 2) * 2];
2331
- y0 = points[(length - 2) * 2 + 1];
2332
- x1 = points[(length - 1) * 2];
2333
- y1 = points[(length - 1) * 2 + 1];
2334
- perpX = -(y0 - y1);
2335
- perpY = x0 - x1;
2336
- dist = Math.sqrt(perpX * perpX + perpY * perpY);
2337
- perpX /= dist;
2338
- perpY /= dist;
2339
- perpX *= width;
2340
- perpY *= width;
2341
- verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
2342
- verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
2343
- if (!closedShape) {
2344
- if (style.cap === "round") {
2345
- indexCount += round(
2346
- x1 - perpX * (innerWeight - outerWeight) * 0.5,
2347
- y1 - perpY * (innerWeight - outerWeight) * 0.5,
2348
- x1 - perpX * innerWeight,
2349
- y1 - perpY * innerWeight,
2350
- x1 + perpX * outerWeight,
2351
- y1 + perpY * outerWeight,
2352
- verts,
2353
- false
2354
- ) + 2;
2355
- } else if (style.cap === "square") {
2356
- indexCount += square(x1, y1, perpX, perpY, innerWeight, outerWeight, false, verts);
2357
- }
2157
+ let scale;
2158
+ {
2159
+ scale = unitConversion[theUnit][defaultUnit];
2160
+ if (scale < 0) {
2161
+ scale = unitConversion[theUnit].in * defaultDPI;
2162
+ }
2163
+ }
2164
+ return scale * Number.parseFloat(string);
2165
+ }
2166
+
2167
+ function getNodeTransform(node, currentTransform, transformStack) {
2168
+ if (!(node.hasAttribute("transform") || node.nodeName === "use" && (node.hasAttribute("x") || node.hasAttribute("y")))) {
2169
+ return null;
2170
+ }
2171
+ const transform = parseNodeTransform(node);
2172
+ if (transformStack.length > 0) {
2173
+ transform.prepend(transformStack[transformStack.length - 1]);
2174
+ }
2175
+ currentTransform.copyFrom(transform);
2176
+ transformStack.push(transform);
2177
+ return transform;
2178
+ }
2179
+ function parseNodeTransform(node) {
2180
+ const transform = new Transform2D();
2181
+ if (node.nodeName === "use" && (node.hasAttribute("x") || node.hasAttribute("y"))) {
2182
+ transform.translate(
2183
+ parseFloatWithUnits(node.getAttribute("x")),
2184
+ parseFloatWithUnits(node.getAttribute("y"))
2185
+ );
2186
+ }
2187
+ if (node.hasAttribute("transform")) {
2188
+ transform.appendCssTransform(node.getAttribute("transform"));
2189
+ }
2190
+ return transform;
2191
+ }
2192
+
2193
+ function parseCircleNode(node) {
2194
+ return new Path2D().arc(
2195
+ parseFloatWithUnits(node.getAttribute("cx") || 0),
2196
+ parseFloatWithUnits(node.getAttribute("cy") || 0),
2197
+ parseFloatWithUnits(node.getAttribute("r") || 0),
2198
+ 0,
2199
+ Math.PI * 2
2200
+ );
2201
+ }
2202
+
2203
+ function parseCSSStylesheet(node, stylesheets) {
2204
+ if (!node.sheet || !node.sheet.cssRules || !node.sheet.cssRules.length)
2205
+ return;
2206
+ for (let i = 0; i < node.sheet.cssRules.length; i++) {
2207
+ const stylesheet = node.sheet.cssRules[i];
2208
+ if (stylesheet.type !== 1)
2209
+ continue;
2210
+ const selectorList = stylesheet.selectorText.split(/,/g).filter(Boolean).map((i2) => i2.trim());
2211
+ const definitions = {};
2212
+ for (let len = stylesheet.style.length, i2 = 0; i2 < len; i2++) {
2213
+ const name = stylesheet.style.item(i2);
2214
+ definitions[name] = stylesheet.style.getPropertyValue(name);
2215
+ }
2216
+ for (let j = 0; j < selectorList.length; j++) {
2217
+ stylesheets[selectorList[j]] = Object.assign(
2218
+ stylesheets[selectorList[j]] || {},
2219
+ { ...definitions }
2220
+ );
2221
+ }
2222
+ }
2223
+ }
2224
+
2225
+ function parseEllipseNode(node) {
2226
+ return new Path2D().ellipse(
2227
+ parseFloatWithUnits(node.getAttribute("cx") || 0),
2228
+ parseFloatWithUnits(node.getAttribute("cy") || 0),
2229
+ parseFloatWithUnits(node.getAttribute("rx") || 0),
2230
+ parseFloatWithUnits(node.getAttribute("ry") || 0),
2231
+ 0,
2232
+ 0,
2233
+ Math.PI * 2
2234
+ );
2235
+ }
2236
+
2237
+ function parseLineNode(node) {
2238
+ return new Path2D().moveTo(
2239
+ parseFloatWithUnits(node.getAttribute("x1") || 0),
2240
+ parseFloatWithUnits(node.getAttribute("y1") || 0)
2241
+ ).lineTo(
2242
+ parseFloatWithUnits(node.getAttribute("x2") || 0),
2243
+ parseFloatWithUnits(node.getAttribute("y2") || 0)
2244
+ );
2245
+ }
2246
+
2247
+ function parsePathNode(node) {
2248
+ const path = new Path2D();
2249
+ const d = node.getAttribute("d");
2250
+ if (!d || d === "none")
2251
+ return null;
2252
+ path.addData(d);
2253
+ return path;
2254
+ }
2255
+
2256
+ const RE$1 = /([+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)(?:,|\s)([+-]?\d*\.?\d+(?:e[+-]?\d+)?)/g;
2257
+ function parsePolygonNode(node) {
2258
+ const path = new Path2D();
2259
+ let index = 0;
2260
+ node.getAttribute("points")?.replace(RE$1, (match, a, b) => {
2261
+ const x = parseFloatWithUnits(a);
2262
+ const y = parseFloatWithUnits(b);
2263
+ if (index === 0) {
2264
+ path.moveTo(x, y);
2265
+ } else {
2266
+ path.lineTo(x, y);
2267
+ }
2268
+ index++;
2269
+ return match;
2270
+ });
2271
+ path.currentCurve.autoClose = true;
2272
+ return path;
2273
+ }
2274
+
2275
+ const RE = /([+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)(?:,|\s)([+-]?\d*\.?\d+(?:e[+-]?\d+)?)/g;
2276
+ function parsePolylineNode(node) {
2277
+ const path = new Path2D();
2278
+ let index = 0;
2279
+ node.getAttribute("points")?.replace(RE, (match, a, b) => {
2280
+ const x = parseFloatWithUnits(a);
2281
+ const y = parseFloatWithUnits(b);
2282
+ if (index === 0) {
2283
+ path.moveTo(x, y);
2284
+ } else {
2285
+ path.lineTo(x, y);
2286
+ }
2287
+ index++;
2288
+ return match;
2289
+ });
2290
+ path.currentCurve.autoClose = false;
2291
+ return path;
2292
+ }
2293
+
2294
+ function parseRectNode(node) {
2295
+ const x = parseFloatWithUnits(node.getAttribute("x") || 0);
2296
+ const y = parseFloatWithUnits(node.getAttribute("y") || 0);
2297
+ const rx = parseFloatWithUnits(node.getAttribute("rx") || node.getAttribute("ry") || 0);
2298
+ const ry = parseFloatWithUnits(node.getAttribute("ry") || node.getAttribute("rx") || 0);
2299
+ const w = parseFloatWithUnits(node.getAttribute("width"));
2300
+ const h = parseFloatWithUnits(node.getAttribute("height"));
2301
+ const bci = 1 - 0.551915024494;
2302
+ const path = new Path2D();
2303
+ path.moveTo(x + rx, y);
2304
+ path.lineTo(x + w - rx, y);
2305
+ if (rx !== 0 || ry !== 0) {
2306
+ path.bezierCurveTo(
2307
+ x + w - rx * bci,
2308
+ y,
2309
+ x + w,
2310
+ y + ry * bci,
2311
+ x + w,
2312
+ y + ry
2313
+ );
2314
+ }
2315
+ path.lineTo(x + w, y + h - ry);
2316
+ if (rx !== 0 || ry !== 0) {
2317
+ path.bezierCurveTo(
2318
+ x + w,
2319
+ y + h - ry * bci,
2320
+ x + w - rx * bci,
2321
+ y + h,
2322
+ x + w - rx,
2323
+ y + h
2324
+ );
2325
+ }
2326
+ path.lineTo(x + rx, y + h);
2327
+ if (rx !== 0 || ry !== 0) {
2328
+ path.bezierCurveTo(
2329
+ x + rx * bci,
2330
+ y + h,
2331
+ x,
2332
+ y + h - ry * bci,
2333
+ x,
2334
+ y + h - ry
2335
+ );
2336
+ }
2337
+ path.lineTo(x, y + ry);
2338
+ if (rx !== 0 || ry !== 0) {
2339
+ path.bezierCurveTo(x, y + ry * bci, x + rx * bci, y, x + rx, y);
2340
+ }
2341
+ return path;
2342
+ }
2343
+
2344
+ function parseStyle(node, style, stylesheets) {
2345
+ style = Object.assign({}, style);
2346
+ let stylesheetStyles = {};
2347
+ if (node.hasAttribute("class")) {
2348
+ const classSelectors = node.getAttribute("class").split(/\s/).filter(Boolean).map((i) => i.trim());
2349
+ for (let i = 0; i < classSelectors.length; i++) {
2350
+ stylesheetStyles = Object.assign(stylesheetStyles, stylesheets[`.${classSelectors[i]}`]);
2351
+ }
2352
+ }
2353
+ if (node.hasAttribute("id")) {
2354
+ stylesheetStyles = Object.assign(stylesheetStyles, stylesheets[`#${node.getAttribute("id")}`]);
2358
2355
  }
2359
- const eps2 = curveEps * curveEps;
2360
- for (let i = indexStart; i < indexCount + indexStart - 2; ++i) {
2361
- x0 = verts[i * 2];
2362
- y0 = verts[i * 2 + 1];
2363
- x1 = verts[(i + 1) * 2];
2364
- y1 = verts[(i + 1) * 2 + 1];
2365
- x2 = verts[(i + 2) * 2];
2366
- y2 = verts[(i + 2) * 2 + 1];
2367
- if (Math.abs(x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)) < eps2) {
2368
- continue;
2369
- }
2370
- indices.push(i, i + 1, i + 2);
2356
+ for (let len = node.style.length, i = 0; i < len; i++) {
2357
+ const name = node.style.item(i);
2358
+ const value = node.style.getPropertyValue(name);
2359
+ style[name] = value;
2360
+ stylesheetStyles[name] = value;
2371
2361
  }
2372
- return {
2373
- vertices,
2374
- indices
2375
- };
2376
- }
2377
- function getOrientationOfPoints(points) {
2378
- const m = points.length;
2379
- if (m < 6) {
2380
- return 1;
2362
+ function addStyle(svgName, jsName, adjustFunction = copy) {
2363
+ if (node.hasAttribute(svgName))
2364
+ style[jsName] = adjustFunction(node.getAttribute(svgName));
2365
+ if (stylesheetStyles[svgName])
2366
+ style[jsName] = adjustFunction(stylesheetStyles[svgName]);
2381
2367
  }
2382
- let area = 0;
2383
- for (let i = 0, x1 = points[m - 2], y1 = points[m - 1]; i < m; i += 2) {
2384
- const x2 = points[i];
2385
- const y2 = points[i + 1];
2386
- area += (x2 - x1) * (y2 + y1);
2387
- x1 = x2;
2388
- y1 = y2;
2368
+ function copy(v) {
2369
+ if (v.startsWith("url"))
2370
+ console.warn("url access in attributes is not implemented.");
2371
+ return v;
2389
2372
  }
2390
- if (area < 0) {
2391
- return -1;
2373
+ function clamp(v) {
2374
+ return Math.max(0, Math.min(1, parseFloatWithUnits(v)));
2392
2375
  }
2393
- return 1;
2394
- }
2395
- function square(x, y, nx, ny, innerWeight, outerWeight, clockwise, verts) {
2396
- const ix = x - nx * innerWeight;
2397
- const iy = y - ny * innerWeight;
2398
- const ox = x + nx * outerWeight;
2399
- const oy = y + ny * outerWeight;
2400
- let exx;
2401
- let eyy;
2402
- if (clockwise) {
2403
- exx = ny;
2404
- eyy = -nx;
2405
- } else {
2406
- exx = -ny;
2407
- eyy = nx;
2376
+ function positive(v) {
2377
+ return Math.max(0, parseFloatWithUnits(v));
2408
2378
  }
2409
- const eix = ix + exx;
2410
- const eiy = iy + eyy;
2411
- const eox = ox + exx;
2412
- const eoy = oy + eyy;
2413
- verts.push(eix, eiy);
2414
- verts.push(eox, eoy);
2415
- return 2;
2416
- }
2417
- function round(cx, cy, sx, sy, ex, ey, verts, clockwise) {
2418
- const cx2p0x = sx - cx;
2419
- const cy2p0y = sy - cy;
2420
- let angle0 = Math.atan2(cx2p0x, cy2p0y);
2421
- let angle1 = Math.atan2(ex - cx, ey - cy);
2422
- if (clockwise && angle0 < angle1) {
2423
- angle0 += Math.PI * 2;
2424
- } else if (!clockwise && angle0 > angle1) {
2425
- angle1 += Math.PI * 2;
2379
+ function array(v) {
2380
+ return v.split(" ").filter((v2) => v2 !== "").map((v2) => parseFloatWithUnits(v2));
2426
2381
  }
2427
- let startAngle = angle0;
2428
- const angleDiff = angle1 - angle0;
2429
- const absAngleDiff = Math.abs(angleDiff);
2430
- const radius = Math.sqrt(cx2p0x * cx2p0x + cy2p0y * cy2p0y);
2431
- const segCount = (15 * absAngleDiff * Math.sqrt(radius) / Math.PI >> 0) + 1;
2432
- const angleInc = angleDiff / segCount;
2433
- startAngle += angleInc;
2434
- if (clockwise) {
2435
- verts.push(cx, cy);
2436
- verts.push(sx, sy);
2437
- for (let i = 1, angle = startAngle; i < segCount; i++, angle += angleInc) {
2438
- verts.push(cx, cy);
2439
- verts.push(cx + Math.sin(angle) * radius, cy + Math.cos(angle) * radius);
2382
+ addStyle("fill", "fill");
2383
+ addStyle("fill-opacity", "fillOpacity", clamp);
2384
+ addStyle("fill-rule", "fillRule");
2385
+ addStyle("opacity", "opacity", clamp);
2386
+ addStyle("stroke", "stroke");
2387
+ addStyle("stroke-opacity", "strokeOpacity", clamp);
2388
+ addStyle("stroke-width", "strokeWidth", positive);
2389
+ addStyle("stroke-linecap", "strokeLinecap");
2390
+ addStyle("stroke-linejoin", "strokeLinejoin");
2391
+ addStyle("stroke-miterlimit", "strokeMiterlimit", positive);
2392
+ addStyle("stroke-dasharray", "strokeDasharray", array);
2393
+ addStyle("stroke-dashoffset", "strokeDashoffset", parseFloatWithUnits);
2394
+ addStyle("visibility", "visibility");
2395
+ return style;
2396
+ }
2397
+
2398
+ function parseNode(node, style, paths = [], stylesheets = {}) {
2399
+ if (node.nodeType !== 1)
2400
+ return paths;
2401
+ let isDefsNode = false;
2402
+ let path = null;
2403
+ let _style = { ...style };
2404
+ switch (node.nodeName) {
2405
+ case "svg":
2406
+ _style = parseStyle(node, _style, stylesheets);
2407
+ break;
2408
+ case "style":
2409
+ parseCSSStylesheet(node, stylesheets);
2410
+ break;
2411
+ case "g":
2412
+ _style = parseStyle(node, _style, stylesheets);
2413
+ break;
2414
+ case "path":
2415
+ _style = parseStyle(node, _style, stylesheets);
2416
+ if (node.hasAttribute("d"))
2417
+ path = parsePathNode(node);
2418
+ break;
2419
+ case "rect":
2420
+ _style = parseStyle(node, _style, stylesheets);
2421
+ path = parseRectNode(node);
2422
+ break;
2423
+ case "polygon":
2424
+ _style = parseStyle(node, _style, stylesheets);
2425
+ path = parsePolygonNode(node);
2426
+ break;
2427
+ case "polyline":
2428
+ _style = parseStyle(node, _style, stylesheets);
2429
+ path = parsePolylineNode(node);
2430
+ break;
2431
+ case "circle":
2432
+ _style = parseStyle(node, _style, stylesheets);
2433
+ path = parseCircleNode(node);
2434
+ break;
2435
+ case "ellipse":
2436
+ _style = parseStyle(node, _style, stylesheets);
2437
+ path = parseEllipseNode(node);
2438
+ break;
2439
+ case "line":
2440
+ _style = parseStyle(node, _style, stylesheets);
2441
+ path = parseLineNode(node);
2442
+ break;
2443
+ case "defs":
2444
+ isDefsNode = true;
2445
+ break;
2446
+ case "use": {
2447
+ _style = parseStyle(node, _style, stylesheets);
2448
+ const href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href") || node.getAttribute("href") || "";
2449
+ const usedNodeId = href.substring(1);
2450
+ const usedNode = node.viewportElement?.getElementById(usedNodeId);
2451
+ if (usedNode) {
2452
+ parseNode(usedNode, _style, paths, stylesheets);
2453
+ } else {
2454
+ console.warn(`'use node' references non-existent node id: ${usedNodeId}`);
2455
+ }
2456
+ break;
2440
2457
  }
2441
- verts.push(cx, cy);
2442
- verts.push(ex, ey);
2443
- } else {
2444
- verts.push(sx, sy);
2445
- verts.push(cx, cy);
2446
- for (let i = 1, angle = startAngle; i < segCount; i++, angle += angleInc) {
2447
- verts.push(cx + Math.sin(angle) * radius, cy + Math.cos(angle) * radius);
2448
- verts.push(cx, cy);
2458
+ default:
2459
+ console.warn(node);
2460
+ break;
2461
+ }
2462
+ if (_style.display === "none") {
2463
+ return paths;
2464
+ }
2465
+ const currentTransform = new Transform2D();
2466
+ const transformStack = [];
2467
+ const transform = getNodeTransform(node, currentTransform, transformStack);
2468
+ if (path) {
2469
+ path.applyTransform(currentTransform);
2470
+ paths.push(path);
2471
+ path.style = { ..._style };
2472
+ }
2473
+ const childNodes = node.childNodes;
2474
+ for (let i = 0, len = childNodes.length; i < len; i++) {
2475
+ const node2 = childNodes[i];
2476
+ if (isDefsNode && node2.nodeName !== "style" && node2.nodeName !== "defs")
2477
+ continue;
2478
+ parseNode(node2, _style, paths, stylesheets);
2479
+ }
2480
+ if (transform) {
2481
+ transformStack.pop();
2482
+ if (transformStack.length > 0) {
2483
+ currentTransform.copyFrom(transformStack[transformStack.length - 1]);
2484
+ } else {
2485
+ currentTransform.identity();
2449
2486
  }
2450
- verts.push(ex, ey);
2451
- verts.push(cx, cy);
2452
2487
  }
2453
- return segCount * 2;
2488
+ return paths;
2489
+ }
2490
+
2491
+ function svgToPath2DSet(svg) {
2492
+ const dom = svgToDom(svg);
2493
+ return new Path2DSet(
2494
+ parseNode(dom, {}),
2495
+ dom.getAttribute("viewBox")?.trim().split(" ").map((v) => Number(v))
2496
+ );
2454
2497
  }
2455
2498
 
2456
2499
  class Curve {
@@ -4480,6 +4523,9 @@ exports.getDirectedArea = getDirectedArea;
4480
4523
  exports.getIntersectionPoint = getIntersectionPoint;
4481
4524
  exports.nonzeroFillRule = nonzeroFillRule;
4482
4525
  exports.parseArcCommand = parseArcCommand;
4526
+ exports.parseCssArg = parseCssArg;
4527
+ exports.parseCssArgs = parseCssArgs;
4528
+ exports.parseCssFunctions = parseCssFunctions;
4483
4529
  exports.parsePathDataArgs = parsePathDataArgs;
4484
4530
  exports.quadraticBezier = quadraticBezier;
4485
4531
  exports.setCanvasContext = setCanvasContext;