modern-path2d 1.5.1 → 1.5.3

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) {
@@ -1351,27 +1204,35 @@ class Transform2D {
1351
1204
  this.ty = tx1 * t2d.b + this.ty * t2d.d + t2d.ty;
1352
1205
  return this;
1353
1206
  }
1354
- _skewX;
1355
- _skewY;
1356
- _skewXTan = 1;
1357
- _skewYTan = 1;
1358
1207
  skewX(x) {
1359
- return this.skew(x, 0);
1208
+ const tan = Math.tan(x);
1209
+ this.a += tan * this.b;
1210
+ this.c += tan * this.d;
1211
+ this.tx += tan * this.ty;
1212
+ return this;
1360
1213
  }
1361
1214
  skewY(y) {
1362
- return this.skew(0, y);
1215
+ const tan = Math.tan(y);
1216
+ this.b += tan * this.a;
1217
+ this.d += tan * this.c;
1218
+ this.ty += tan * this.tx;
1219
+ return this;
1363
1220
  }
1364
1221
  skew(x, y) {
1365
- if (x !== this._skewX) {
1366
- this._skewX = x;
1367
- this._skewXTan = Math.tan(x);
1368
- }
1369
- if (y !== this._skewY) {
1370
- this._skewY = y;
1371
- this._skewYTan = Math.tan(y);
1372
- }
1373
- this.b *= this._skewXTan;
1374
- this.c *= this._skewYTan;
1222
+ const tanX = Math.tan(x);
1223
+ const tanY = Math.tan(y);
1224
+ const a1 = this.a;
1225
+ const b1 = this.b;
1226
+ const c1 = this.c;
1227
+ const d1 = this.d;
1228
+ const tx1 = this.tx;
1229
+ const ty1 = this.ty;
1230
+ this.a = a1 + tanX * b1;
1231
+ this.b = tanY * a1 + b1;
1232
+ this.c = c1 + tanX * d1;
1233
+ this.d = tanY * c1 + d1;
1234
+ this.tx = tx1 + tanX * ty1;
1235
+ this.ty = tanY * tx1 + ty1;
1375
1236
  return this;
1376
1237
  }
1377
1238
  translateX(x) {
@@ -1558,115 +1419,78 @@ class Transform2D {
1558
1419
  equals(t2d) {
1559
1420
  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
1421
  }
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
- }
1422
+ appendCssTransform(cssTransform, ctx = {}) {
1423
+ const { width = 1, height = 1 } = ctx;
1424
+ const temp = new Transform2D();
1425
+ parseCssFunctions(cssTransform, { width, height }).reverse().forEach(({ name, args }) => {
1426
+ const values = args.map((arg) => arg.normalizedIntValue);
1427
+ switch (name) {
1428
+ case "translate":
1429
+ temp.translate(values[0] * width, (values[1] ?? values[0]) * height);
1430
+ break;
1431
+ case "translateX":
1432
+ temp.translateX(values[0] * width);
1433
+ break;
1434
+ case "translateY":
1435
+ temp.translateY(values[0] * height);
1436
+ break;
1437
+ case "translateZ":
1438
+ temp.translateZ(values[0]);
1439
+ break;
1440
+ case "translate3d":
1441
+ temp.translate3d(
1442
+ values[0] * width,
1443
+ (values[1] ?? values[0]) * height,
1444
+ values[2] ?? values[1] ?? values[0]
1445
+ );
1446
+ break;
1447
+ case "scale":
1448
+ temp.scale(values[0], values[1] ?? values[0]);
1449
+ break;
1450
+ case "scaleX":
1451
+ temp.scaleX(values[0]);
1452
+ break;
1453
+ case "scaleY":
1454
+ temp.scaleY(values[0]);
1455
+ break;
1456
+ case "scale3d":
1457
+ temp.scale3d(values[0], values[1] ?? values[0], values[2] ?? values[1] ?? values[0]);
1458
+ break;
1459
+ case "rotate":
1460
+ temp.rotate(values[0] * PI_2);
1461
+ break;
1462
+ case "rotateX":
1463
+ temp.rotateX(values[0] * PI_2);
1464
+ break;
1465
+ case "rotateY":
1466
+ temp.rotateY(values[0] * PI_2);
1467
+ break;
1468
+ case "rotateZ":
1469
+ temp.rotateZ(values[0] * PI_2);
1470
+ break;
1471
+ case "rotate3d":
1472
+ temp.rotate3d(
1473
+ values[0] * PI_2,
1474
+ (values[1] ?? values[0]) * PI_2,
1475
+ (values[2] ?? values[1] ?? values[0]) * PI_2,
1476
+ (values[3] ?? values[2] ?? values[1] ?? values[0]) * PI_2
1477
+ );
1478
+ break;
1479
+ case "skew":
1480
+ temp.skew(values[0], values[0] ?? values[1]);
1481
+ break;
1482
+ case "skewX":
1483
+ temp.skewX(values[0]);
1484
+ break;
1485
+ case "skewY":
1486
+ temp.skewY(values[0]);
1487
+ break;
1488
+ case "matrix":
1489
+ temp.set(values[0], values[1], values[2], values[3], values[4], values[5]);
1490
+ break;
1667
1491
  }
1668
- this.prepend(transform);
1669
- }
1492
+ });
1493
+ this.prepend(temp);
1670
1494
  return this;
1671
1495
  }
1672
1496
  clone() {
@@ -1703,754 +1527,991 @@ class Transform2D {
1703
1527
  toString() {
1704
1528
  return `[Transform2D a=${this.a} b=${this.b} c=${this.c} d=${this.d} tx=${this.tx} ty=${this.ty}]`;
1705
1529
  }
1530
+ toJSON() {
1531
+ return {
1532
+ a: this.a,
1533
+ b: this.b,
1534
+ c: this.c,
1535
+ d: this.d,
1536
+ tx: this.tx,
1537
+ ty: this.ty
1538
+ };
1539
+ }
1706
1540
  destroy() {
1707
1541
  this._array = void 0;
1708
1542
  }
1709
1543
  }
1710
1544
 
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;
1545
+ function svgAngle(ux, uy, vx, vy) {
1546
+ const dot = ux * vx + uy * vy;
1547
+ const len = Math.sqrt(ux * ux + uy * uy) * Math.sqrt(vx * vx + vy * vy);
1548
+ let ang = Math.acos(Math.max(-1, Math.min(1, dot / len)));
1549
+ if (ux * vy - uy * vx < 0)
1550
+ ang = -ang;
1551
+ return ang;
1732
1552
  }
1733
- function cubicBezier(t, p0, p1, p2, p3) {
1734
- return cubicBezierP0(t, p0) + cubicBezierP1(t, p1) + cubicBezierP2(t, p2) + cubicBezierP3(t, p3);
1553
+ function parseArcCommand(path, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, start, end) {
1554
+ if (rx === 0 || ry === 0) {
1555
+ path.lineTo(end.x, end.y);
1556
+ return;
1557
+ }
1558
+ xAxisRotation = xAxisRotation * Math.PI / 180;
1559
+ rx = Math.abs(rx);
1560
+ ry = Math.abs(ry);
1561
+ const dx2 = (start.x - end.x) / 2;
1562
+ const dy2 = (start.y - end.y) / 2;
1563
+ const x1p = Math.cos(xAxisRotation) * dx2 + Math.sin(xAxisRotation) * dy2;
1564
+ const y1p = -Math.sin(xAxisRotation) * dx2 + Math.cos(xAxisRotation) * dy2;
1565
+ let rxs = rx * rx;
1566
+ let rys = ry * ry;
1567
+ const x1ps = x1p * x1p;
1568
+ const y1ps = y1p * y1p;
1569
+ const cr = x1ps / rxs + y1ps / rys;
1570
+ if (cr > 1) {
1571
+ const s = Math.sqrt(cr);
1572
+ rx = s * rx;
1573
+ ry = s * ry;
1574
+ rxs = rx * rx;
1575
+ rys = ry * ry;
1576
+ }
1577
+ const dq = rxs * y1ps + rys * x1ps;
1578
+ const pq = (rxs * rys - dq) / dq;
1579
+ let q = Math.sqrt(Math.max(0, pq));
1580
+ if (largeArcFlag === sweepFlag)
1581
+ q = -q;
1582
+ const cxp = q * rx * y1p / ry;
1583
+ const cyp = -q * ry * x1p / rx;
1584
+ const cx = Math.cos(xAxisRotation) * cxp - Math.sin(xAxisRotation) * cyp + (start.x + end.x) / 2;
1585
+ const cy = Math.sin(xAxisRotation) * cxp + Math.cos(xAxisRotation) * cyp + (start.y + end.y) / 2;
1586
+ const theta = svgAngle(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);
1587
+ const delta = svgAngle((x1p - cxp) / rx, (y1p - cyp) / ry, (-x1p - cxp) / rx, (-y1p - cyp) / ry) % (Math.PI * 2);
1588
+ path.ellipse(cx, cy, rx, ry, xAxisRotation, theta, theta + delta, sweepFlag === 0);
1735
1589
  }
1736
1590
 
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;
1591
+ const RE$3 = {
1592
+ WHITESPACE: /[ \t\r\n]/,
1593
+ DIGIT: /\d/,
1594
+ SIGN: /[-+]/,
1595
+ POINT: /\./,
1596
+ COMMA: /,/,
1597
+ EXP: /e/i,
1598
+ FLAGS: /[01]/
1599
+ };
1600
+ function parsePathDataArgs(input, flags, stride = 0) {
1601
+ const SEP = 0;
1602
+ const INT = 1;
1603
+ const FLOAT = 2;
1604
+ const EXP = 3;
1605
+ let state = SEP;
1606
+ let seenComma = true;
1607
+ let number = "";
1608
+ let exponent = "";
1609
+ const result = [];
1610
+ function throwSyntaxError(current2, i, partial) {
1611
+ const error = new SyntaxError(`Unexpected character "${current2}" at index ${i}.`);
1612
+ error.partial = partial;
1613
+ throw error;
1614
+ }
1615
+ function newNumber() {
1616
+ if (number !== "") {
1617
+ if (exponent === "")
1618
+ result.push(Number(number));
1619
+ else result.push(Number(number) * 10 ** Number(exponent));
1758
1620
  }
1621
+ number = "";
1622
+ exponent = "";
1759
1623
  }
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
- }
1624
+ let current;
1625
+ const length = input.length;
1626
+ for (let i = 0; i < length; i++) {
1627
+ current = input[i];
1628
+ if (Array.isArray(flags) && flags.includes(result.length % stride) && RE$3.FLAGS.test(current)) {
1629
+ state = INT;
1630
+ number = current;
1631
+ newNumber();
1632
+ continue;
1633
+ }
1634
+ if (state === SEP) {
1635
+ if (RE$3.WHITESPACE.test(current)) {
1636
+ continue;
1808
1637
  }
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
- }
1638
+ if (RE$3.DIGIT.test(current) || RE$3.SIGN.test(current)) {
1639
+ state = INT;
1640
+ number = current;
1641
+ continue;
1815
1642
  }
1816
- } else if (d3 > FLT_EPSILON$1) {
1817
- if (d3 * d3 <= distanceTolerance * (dx * dx + dy * dy)) {
1818
- {
1819
- points.push(x1234, y1234);
1820
- return;
1643
+ if (RE$3.POINT.test(current)) {
1644
+ state = FLOAT;
1645
+ number = current;
1646
+ continue;
1647
+ }
1648
+ if (RE$3.COMMA.test(current)) {
1649
+ if (seenComma) {
1650
+ throwSyntaxError(current, i, result);
1821
1651
  }
1652
+ seenComma = true;
1822
1653
  }
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;
1654
+ }
1655
+ if (state === INT) {
1656
+ if (RE$3.DIGIT.test(current)) {
1657
+ number += current;
1658
+ continue;
1659
+ }
1660
+ if (RE$3.POINT.test(current)) {
1661
+ number += current;
1662
+ state = FLOAT;
1663
+ continue;
1664
+ }
1665
+ if (RE$3.EXP.test(current)) {
1666
+ state = EXP;
1667
+ continue;
1668
+ }
1669
+ if (RE$3.SIGN.test(current) && number.length === 1 && RE$3.SIGN.test(number[0])) {
1670
+ throwSyntaxError(current, i, result);
1829
1671
  }
1830
1672
  }
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;
1673
+ if (state === FLOAT) {
1674
+ if (RE$3.DIGIT.test(current)) {
1675
+ number += current;
1676
+ continue;
1677
+ }
1678
+ if (RE$3.EXP.test(current)) {
1679
+ state = EXP;
1680
+ continue;
1681
+ }
1682
+ if (RE$3.POINT.test(current) && number[number.length - 1] === ".") {
1683
+ throwSyntaxError(current, i, result);
1869
1684
  }
1870
1685
  }
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;
1686
+ if (state === EXP) {
1687
+ if (RE$3.DIGIT.test(current)) {
1688
+ exponent += current;
1689
+ continue;
1690
+ }
1691
+ if (RE$3.SIGN.test(current)) {
1692
+ if (exponent === "") {
1693
+ exponent += current;
1694
+ continue;
1695
+ }
1696
+ if (exponent.length === 1 && RE$3.SIGN.test(exponent)) {
1697
+ throwSyntaxError(current, i, result);
1698
+ }
1699
+ }
1877
1700
  }
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++;
1701
+ if (RE$3.WHITESPACE.test(current)) {
1702
+ newNumber();
1703
+ state = SEP;
1704
+ seenComma = false;
1705
+ } else if (RE$3.COMMA.test(current)) {
1706
+ newNumber();
1707
+ state = SEP;
1708
+ seenComma = true;
1709
+ } else if (RE$3.SIGN.test(current)) {
1710
+ newNumber();
1711
+ state = INT;
1712
+ number = current;
1713
+ } else if (RE$3.POINT.test(current)) {
1714
+ newNumber();
1715
+ state = FLOAT;
1716
+ number = current;
1937
1717
  } else {
1938
- if (yj <= py && cross(xj, yj, xi, yi, px, py) < 0)
1939
- wn--;
1718
+ throwSyntaxError(current, i, result);
1940
1719
  }
1941
1720
  }
1942
- return wn;
1721
+ newNumber();
1722
+ return result;
1943
1723
  }
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);
1724
+
1725
+ function getReflection(a, b) {
1726
+ return a - (b - a);
1948
1727
  }
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];
1728
+ function svgPathCommandsAddToPath2D(commands, path) {
1729
+ const current = new Vector2();
1730
+ const control = new Vector2();
1731
+ for (let i = 0, l = commands.length; i < l; i++) {
1732
+ const cmd = commands[i];
1733
+ if (cmd.type === "m" || cmd.type === "M") {
1734
+ if (cmd.type === "m") {
1735
+ current.add(cmd);
1736
+ } else {
1737
+ current.copyFrom(cmd);
1965
1738
  }
1966
- if (xAutoYMin[1] > y) {
1967
- xAutoYMin = [x, y];
1739
+ path.moveTo(current.x, current.y);
1740
+ control.copyFrom(current);
1741
+ } else if (cmd.type === "h" || cmd.type === "H") {
1742
+ if (cmd.type === "h") {
1743
+ current.x += cmd.x;
1744
+ } else {
1745
+ current.x = cmd.x;
1968
1746
  }
1969
- if (xMaxYAuto[0] < x) {
1970
- xMaxYAuto = [x, y];
1747
+ path.lineTo(current.x, current.y);
1748
+ control.copyFrom(current);
1749
+ } else if (cmd.type === "v" || cmd.type === "V") {
1750
+ if (cmd.type === "v") {
1751
+ current.y += cmd.y;
1752
+ } else {
1753
+ current.y = cmd.y;
1971
1754
  }
1972
- if (xAutoYMax[1] < y) {
1973
- xAutoYMax = [x, y];
1755
+ path.lineTo(current.x, current.y);
1756
+ control.copyFrom(current);
1757
+ } else if (cmd.type === "l" || cmd.type === "L") {
1758
+ if (cmd.type === "l") {
1759
+ current.add(cmd);
1760
+ } else {
1761
+ current.copyFrom(cmd);
1974
1762
  }
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];
1763
+ path.lineTo(current.x, current.y);
1764
+ control.copyFrom(current);
1765
+ } else if (cmd.type === "c" || cmd.type === "C") {
1766
+ if (cmd.type === "c") {
1767
+ path.bezierCurveTo(
1768
+ current.x + cmd.x1,
1769
+ current.y + cmd.y1,
1770
+ current.x + cmd.x2,
1771
+ current.y + cmd.y2,
1772
+ current.x + cmd.x,
1773
+ current.y + cmd.y
1774
+ );
1775
+ control.x = current.x + cmd.x2;
1776
+ control.y = current.y + cmd.y2;
1777
+ current.add(cmd);
1778
+ } else {
1779
+ path.bezierCurveTo(
1780
+ cmd.x1,
1781
+ cmd.y1,
1782
+ cmd.x2,
1783
+ cmd.y2,
1784
+ cmd.x,
1785
+ cmd.y
1786
+ );
1787
+ control.x = cmd.x2;
1788
+ control.y = cmd.y2;
1789
+ current.copyFrom(cmd);
1996
1790
  }
1997
- if (y > mid[1] && (!xMidYMaxDx || _dx < xMidYMaxDx)) {
1998
- xMidYMaxDx = _dx;
1999
- xMidYMax = [x, y];
1791
+ } else if (cmd.type === "s" || cmd.type === "S") {
1792
+ if (cmd.type === "s") {
1793
+ path.bezierCurveTo(
1794
+ getReflection(current.x, control.x),
1795
+ getReflection(current.y, control.y),
1796
+ current.x + cmd.x2,
1797
+ current.y + cmd.y2,
1798
+ current.x + cmd.x,
1799
+ current.y + cmd.y
1800
+ );
1801
+ control.x = current.x + cmd.x2;
1802
+ control.y = current.y + cmd.y2;
1803
+ current.add(cmd);
1804
+ } else {
1805
+ path.bezierCurveTo(
1806
+ getReflection(current.x, control.x),
1807
+ getReflection(current.y, control.y),
1808
+ cmd.x2,
1809
+ cmd.y2,
1810
+ cmd.x,
1811
+ cmd.y
1812
+ );
1813
+ control.x = cmd.x2;
1814
+ control.y = cmd.y2;
1815
+ current.copyFrom(cmd);
2000
1816
  }
2001
- if (x < mid[0] && (!xMinYMidDy || _dy < xMinYMidDy)) {
2002
- xMinYMidDy = _dy;
2003
- xMinYMid = [x, y];
1817
+ } else if (cmd.type === "q" || cmd.type === "Q") {
1818
+ if (cmd.type === "q") {
1819
+ path.quadraticCurveTo(
1820
+ current.x + cmd.x1,
1821
+ current.y + cmd.y1,
1822
+ current.x + cmd.x,
1823
+ current.y + cmd.y
1824
+ );
1825
+ control.x = current.x + cmd.x1;
1826
+ control.y = current.y + cmd.y1;
1827
+ current.add(cmd);
1828
+ } else {
1829
+ path.quadraticCurveTo(
1830
+ cmd.x1,
1831
+ cmd.y1,
1832
+ cmd.x,
1833
+ cmd.y
1834
+ );
1835
+ control.x = cmd.x1;
1836
+ control.y = cmd.y1;
1837
+ current.copyFrom(cmd);
2004
1838
  }
2005
- if (x > mid[0] && (!xMaxYMidDy || _dy < xMaxYMidDy)) {
2006
- xMaxYMidDy = _dy;
2007
- xMaxYMid = [x, y];
1839
+ } else if (cmd.type === "t" || cmd.type === "T") {
1840
+ const rx = getReflection(current.x, control.x);
1841
+ const ry = getReflection(current.y, control.y);
1842
+ control.x = rx;
1843
+ control.y = ry;
1844
+ if (cmd.type === "t") {
1845
+ path.quadraticCurveTo(
1846
+ rx,
1847
+ ry,
1848
+ current.x + cmd.x,
1849
+ current.y + cmd.y
1850
+ );
1851
+ current.add(cmd);
1852
+ } else {
1853
+ path.quadraticCurveTo(
1854
+ rx,
1855
+ ry,
1856
+ cmd.x,
1857
+ cmd.y
1858
+ );
1859
+ current.copyFrom(cmd);
2008
1860
  }
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);
1861
+ } else if (cmd.type === "a" || cmd.type === "A") {
1862
+ const start = current.clone();
1863
+ if (cmd.type === "a") {
1864
+ if (cmd.x === 0 && cmd.y === 0)
1865
+ continue;
1866
+ current.add(cmd);
1867
+ } else {
1868
+ if (current.equals(cmd))
1869
+ continue;
1870
+ current.copyFrom(cmd);
2034
1871
  }
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
- });
1872
+ control.copyFrom(current);
1873
+ parseArcCommand(
1874
+ path,
1875
+ cmd.rx,
1876
+ cmd.ry,
1877
+ cmd.angle,
1878
+ cmd.largeArcFlag,
1879
+ cmd.sweepFlag,
1880
+ start,
1881
+ current
1882
+ );
1883
+ } else if (cmd.type === "z" || cmd.type === "Z") {
1884
+ if (path.startPoint) {
1885
+ current.copyFrom(path.startPoint);
2044
1886
  }
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];
1887
+ path.closePath();
1888
+ } else {
1889
+ console.warn("Unsupported commands", cmd);
2049
1890
  }
2050
1891
  }
2051
- return results;
2052
1892
  }
2053
1893
 
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);
1894
+ function svgPathCommandsToData(commands) {
1895
+ let first;
1896
+ let prev;
1897
+ const data = [];
1898
+ for (let i = 0, len = commands.length; i < len; i++) {
1899
+ const cmd = commands[i];
1900
+ switch (cmd.type) {
1901
+ case "m":
1902
+ case "M":
1903
+ if (cmd.x.toFixed(4) === prev?.x.toFixed(4) && cmd.y.toFixed(4) === prev?.y.toFixed(4)) {
1904
+ continue;
1905
+ }
1906
+ data.push(`${cmd.type} ${cmd.x} ${cmd.y}`);
1907
+ prev = { x: cmd.x, y: cmd.y };
1908
+ first = { x: cmd.x, y: cmd.y };
1909
+ break;
1910
+ case "h":
1911
+ case "H":
1912
+ data.push(`${cmd.type} ${cmd.x}`);
1913
+ prev = { x: cmd.x, y: prev?.y ?? 0 };
1914
+ break;
1915
+ case "v":
1916
+ case "V":
1917
+ data.push(`${cmd.type} ${cmd.y}`);
1918
+ prev = { x: prev?.x ?? 0, y: cmd.y };
1919
+ break;
1920
+ case "l":
1921
+ case "L":
1922
+ data.push(`${cmd.type} ${cmd.x} ${cmd.y}`);
1923
+ prev = { x: cmd.x, y: cmd.y };
1924
+ break;
1925
+ case "c":
1926
+ case "C":
1927
+ data.push(`${cmd.type} ${cmd.x1} ${cmd.y1} ${cmd.x2} ${cmd.y2} ${cmd.x} ${cmd.y}`);
1928
+ prev = { x: cmd.x, y: cmd.y };
1929
+ break;
1930
+ case "s":
1931
+ case "S":
1932
+ data.push(`${cmd.type} ${cmd.x2} ${cmd.y2} ${cmd.x} ${cmd.y}`);
1933
+ prev = { x: cmd.x, y: cmd.y };
1934
+ break;
1935
+ case "q":
1936
+ case "Q":
1937
+ data.push(`${cmd.type} ${cmd.x1} ${cmd.y1} ${cmd.x} ${cmd.y}`);
1938
+ prev = { x: cmd.x, y: cmd.y };
1939
+ break;
1940
+ case "t":
1941
+ case "T":
1942
+ data.push(`${cmd.type} ${cmd.x} ${cmd.y}`);
1943
+ prev = { x: cmd.x, y: cmd.y };
1944
+ break;
1945
+ case "a":
1946
+ case "A":
1947
+ data.push(`${cmd.type} ${cmd.rx} ${cmd.ry} ${cmd.angle} ${cmd.largeArcFlag} ${cmd.sweepFlag} ${cmd.x} ${cmd.y}`);
1948
+ prev = { x: cmd.x, y: cmd.y };
1949
+ break;
1950
+ case "z":
1951
+ case "Z":
1952
+ data.push(cmd.type);
1953
+ if (first) {
1954
+ prev = { x: first.x, y: first.y };
1955
+ }
1956
+ break;
1957
+ }
1958
+ }
1959
+ return data.join(" ");
2066
1960
  }
2067
1961
 
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;
1962
+ const RE$2 = /[a-df-z][^a-df-z]*/gi;
1963
+ function svgPathDataToCommands(data) {
1964
+ const commands = [];
1965
+ const matched = data.match(RE$2);
1966
+ if (!matched) {
1967
+ return commands;
2095
1968
  }
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];
1969
+ for (let i = 0, len = matched.length; i < len; i++) {
1970
+ const command = matched[i];
1971
+ const type = command.charAt(0);
1972
+ const data2 = command.slice(1).trim();
1973
+ let args;
1974
+ switch (type) {
1975
+ case "m":
1976
+ case "M":
1977
+ args = parsePathDataArgs(data2);
1978
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 2) {
1979
+ if (i2 === 0) {
1980
+ commands.push({ type, x: args[i2], y: args[i2 + 1] });
1981
+ } else {
1982
+ commands.push({ type: type === "m" ? "l" : "L", x: args[i2], y: args[i2 + 1] });
1983
+ }
1984
+ }
1985
+ break;
1986
+ case "h":
1987
+ case "H":
1988
+ args = parsePathDataArgs(data2);
1989
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2++) {
1990
+ commands.push({ type, x: args[i2] });
1991
+ }
1992
+ break;
1993
+ case "v":
1994
+ case "V":
1995
+ args = parsePathDataArgs(data2);
1996
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2++) {
1997
+ commands.push({ type, y: args[i2] });
1998
+ }
1999
+ break;
2000
+ case "l":
2001
+ case "L":
2002
+ args = parsePathDataArgs(data2);
2003
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 2) {
2004
+ commands.push({ type, x: args[i2], y: args[i2 + 1] });
2005
+ }
2006
+ break;
2007
+ case "c":
2008
+ case "C":
2009
+ args = parsePathDataArgs(data2);
2010
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 6) {
2011
+ commands.push({
2012
+ type,
2013
+ x1: args[i2],
2014
+ y1: args[i2 + 1],
2015
+ x2: args[i2 + 2],
2016
+ y2: args[i2 + 3],
2017
+ x: args[i2 + 4],
2018
+ y: args[i2 + 5]
2019
+ });
2020
+ }
2021
+ break;
2022
+ case "s":
2023
+ case "S":
2024
+ args = parsePathDataArgs(data2);
2025
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 4) {
2026
+ commands.push({
2027
+ type,
2028
+ x2: args[i2],
2029
+ y2: args[i2 + 1],
2030
+ x: args[i2 + 2],
2031
+ y: args[i2 + 3]
2032
+ });
2033
+ }
2034
+ break;
2035
+ case "q":
2036
+ case "Q":
2037
+ args = parsePathDataArgs(data2);
2038
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 4) {
2039
+ commands.push({
2040
+ type,
2041
+ x1: args[i2],
2042
+ y1: args[i2 + 1],
2043
+ x: args[i2 + 2],
2044
+ y: args[i2 + 3]
2045
+ });
2046
+ }
2047
+ break;
2048
+ case "t":
2049
+ case "T":
2050
+ args = parsePathDataArgs(data2);
2051
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 2) {
2052
+ commands.push({
2053
+ type,
2054
+ x: args[i2],
2055
+ y: args[i2 + 1]
2056
+ });
2057
+ }
2058
+ break;
2059
+ case "a":
2060
+ case "A":
2061
+ args = parsePathDataArgs(data2, [3, 4], 7);
2062
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 7) {
2063
+ commands.push({
2064
+ type,
2065
+ rx: args[i2],
2066
+ ry: args[i2 + 1],
2067
+ angle: args[i2 + 2],
2068
+ largeArcFlag: args[i2 + 3],
2069
+ sweepFlag: args[i2 + 4],
2070
+ x: args[i2 + 5],
2071
+ y: args[i2 + 6]
2072
+ });
2073
+ }
2074
+ break;
2075
+ case "z":
2076
+ case "Z":
2077
+ commands.push({
2078
+ type
2079
+ });
2080
+ break;
2081
+ default:
2082
+ console.warn(command);
2107
2083
  }
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
2084
  }
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);
2085
+ return commands;
2086
+ }
2087
+
2088
+ const dataUri = "data:image/svg+xml;";
2089
+ const base64DataUri = `${dataUri}base64,`;
2090
+ const utf8DataUri = `${dataUri}charset=utf8,`;
2091
+ function svgToDom(svg) {
2092
+ if (typeof svg === "string") {
2093
+ let xml;
2094
+ if (svg.startsWith(base64DataUri)) {
2095
+ svg = svg.substring(base64DataUri.length, svg.length);
2096
+ xml = atob(svg);
2097
+ } else if (svg.startsWith(utf8DataUri)) {
2098
+ svg = svg.substring(utf8DataUri.length, svg.length);
2099
+ xml = decodeURIComponent(svg);
2100
+ } else {
2101
+ xml = svg;
2102
+ }
2103
+ const doc = new DOMParser().parseFromString(xml, "text/xml");
2104
+ const error = doc.querySelector("parsererror");
2105
+ if (error) {
2106
+ throw new Error(`${error.textContent ?? "parser error"}
2107
+ ${xml}`);
2152
2108
  }
2109
+ return doc.documentElement;
2110
+ } else {
2111
+ return svg;
2153
2112
  }
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;
2213
- }
2214
- verts.push(
2215
- x1 - perp1x * outerWeight,
2216
- y1 - perp1y * outerWeight
2217
- );
2218
- verts.push(
2219
- x1 + perp1x * innerWeight,
2220
- y1 + perp1y * innerWeight
2221
- );
2113
+ }
2114
+
2115
+ const defaultUnit = "px";
2116
+ const defaultDPI = 90;
2117
+ const units = ["mm", "cm", "in", "pt", "pc", "px"];
2118
+ const unitConversion = {
2119
+ mm: {
2120
+ mm: 1,
2121
+ cm: 0.1,
2122
+ in: 1 / 25.4,
2123
+ pt: 72 / 25.4,
2124
+ pc: 6 / 25.4,
2125
+ px: -1
2126
+ },
2127
+ cm: {
2128
+ mm: 10,
2129
+ cm: 1,
2130
+ in: 1 / 2.54,
2131
+ pt: 72 / 2.54,
2132
+ pc: 6 / 2.54,
2133
+ px: -1
2134
+ },
2135
+ in: {
2136
+ mm: 25.4,
2137
+ cm: 2.54,
2138
+ in: 1,
2139
+ pt: 72,
2140
+ pc: 6,
2141
+ px: -1
2142
+ },
2143
+ pt: {
2144
+ mm: 25.4 / 72,
2145
+ cm: 2.54 / 72,
2146
+ in: 1 / 72,
2147
+ pt: 1,
2148
+ pc: 6 / 72,
2149
+ px: -1
2150
+ },
2151
+ pc: {
2152
+ mm: 25.4 / 6,
2153
+ cm: 2.54 / 6,
2154
+ in: 1 / 6,
2155
+ pt: 72 / 6,
2156
+ pc: 1,
2157
+ px: -1
2158
+ },
2159
+ px: {
2160
+ px: 1
2161
+ }
2162
+ };
2163
+ function parseFloatWithUnits(string) {
2164
+ let theUnit = "px";
2165
+ if (typeof string === "string") {
2166
+ for (let i = 0, n = units.length; i < n; i++) {
2167
+ const u = units[i];
2168
+ if (string.endsWith(u)) {
2169
+ theUnit = u;
2170
+ string = string.substring(0, string.length - u.length);
2171
+ break;
2222
2172
  }
2173
+ }
2174
+ }
2175
+ let scale;
2176
+ {
2177
+ scale = unitConversion[theUnit][defaultUnit];
2178
+ if (scale < 0) {
2179
+ scale = unitConversion[theUnit].in * defaultDPI;
2180
+ }
2181
+ }
2182
+ return scale * Number.parseFloat(string);
2183
+ }
2184
+
2185
+ function getNodeTransform(node, currentTransform, transformStack) {
2186
+ if (!(node.hasAttribute("transform") || node.nodeName === "use" && (node.hasAttribute("x") || node.hasAttribute("y")))) {
2187
+ return null;
2188
+ }
2189
+ const transform = parseNodeTransform(node);
2190
+ if (transformStack.length > 0) {
2191
+ transform.prepend(transformStack[transformStack.length - 1]);
2192
+ }
2193
+ currentTransform.copyFrom(transform);
2194
+ transformStack.push(transform);
2195
+ return transform;
2196
+ }
2197
+ function parseNodeTransform(node) {
2198
+ const transform = new Transform2D();
2199
+ if (node.nodeName === "use" && (node.hasAttribute("x") || node.hasAttribute("y"))) {
2200
+ transform.translate(
2201
+ parseFloatWithUnits(node.getAttribute("x")),
2202
+ parseFloatWithUnits(node.getAttribute("y"))
2203
+ );
2204
+ }
2205
+ if (node.hasAttribute("transform")) {
2206
+ transform.appendCssTransform(node.getAttribute("transform"));
2207
+ }
2208
+ return transform;
2209
+ }
2210
+
2211
+ function parseCircleNode(node) {
2212
+ return new Path2D().arc(
2213
+ parseFloatWithUnits(node.getAttribute("cx") || 0),
2214
+ parseFloatWithUnits(node.getAttribute("cy") || 0),
2215
+ parseFloatWithUnits(node.getAttribute("r") || 0),
2216
+ 0,
2217
+ Math.PI * 2
2218
+ );
2219
+ }
2220
+
2221
+ function parseCSSStylesheet(node, stylesheets) {
2222
+ if (!node.sheet || !node.sheet.cssRules || !node.sheet.cssRules.length)
2223
+ return;
2224
+ for (let i = 0; i < node.sheet.cssRules.length; i++) {
2225
+ const stylesheet = node.sheet.cssRules[i];
2226
+ if (stylesheet.type !== 1)
2223
2227
  continue;
2228
+ const selectorList = stylesheet.selectorText.split(/,/g).filter(Boolean).map((i2) => i2.trim());
2229
+ const definitions = {};
2230
+ for (let len = stylesheet.style.length, i2 = 0; i2 < len; i2++) {
2231
+ const name = stylesheet.style.item(i2);
2232
+ definitions[name] = stylesheet.style.getPropertyValue(name);
2224
2233
  }
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);
2250
- }
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);
2283
- }
2284
- } else {
2285
- verts.push(imx, imy);
2286
- verts.push(omx, omy);
2287
- }
2234
+ for (let j = 0; j < selectorList.length; j++) {
2235
+ stylesheets[selectorList[j]] = Object.assign(
2236
+ stylesheets[selectorList[j]] || {},
2237
+ { ...definitions }
2238
+ );
2239
+ }
2240
+ }
2241
+ }
2242
+
2243
+ function parseEllipseNode(node) {
2244
+ return new Path2D().ellipse(
2245
+ parseFloatWithUnits(node.getAttribute("cx") || 0),
2246
+ parseFloatWithUnits(node.getAttribute("cy") || 0),
2247
+ parseFloatWithUnits(node.getAttribute("rx") || 0),
2248
+ parseFloatWithUnits(node.getAttribute("ry") || 0),
2249
+ 0,
2250
+ 0,
2251
+ Math.PI * 2
2252
+ );
2253
+ }
2254
+
2255
+ function parseLineNode(node) {
2256
+ return new Path2D().moveTo(
2257
+ parseFloatWithUnits(node.getAttribute("x1") || 0),
2258
+ parseFloatWithUnits(node.getAttribute("y1") || 0)
2259
+ ).lineTo(
2260
+ parseFloatWithUnits(node.getAttribute("x2") || 0),
2261
+ parseFloatWithUnits(node.getAttribute("y2") || 0)
2262
+ );
2263
+ }
2264
+
2265
+ function parsePathNode(node) {
2266
+ const path = new Path2D();
2267
+ const d = node.getAttribute("d");
2268
+ if (!d || d === "none")
2269
+ return null;
2270
+ path.addData(d);
2271
+ return path;
2272
+ }
2273
+
2274
+ const RE$1 = /([+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)(?:,|\s)([+-]?\d*\.?\d+(?:e[+-]?\d+)?)/g;
2275
+ function parsePolygonNode(node) {
2276
+ const path = new Path2D();
2277
+ let index = 0;
2278
+ node.getAttribute("points")?.replace(RE$1, (match, a, b) => {
2279
+ const x = parseFloatWithUnits(a);
2280
+ const y = parseFloatWithUnits(b);
2281
+ if (index === 0) {
2282
+ path.moveTo(x, y);
2288
2283
  } 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;
2314
- }
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);
2322
- }
2323
- indexCount += 2;
2324
- }
2325
- verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
2326
- verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
2327
- indexCount += 2;
2284
+ path.lineTo(x, y);
2285
+ }
2286
+ index++;
2287
+ return match;
2288
+ });
2289
+ path.currentCurve.autoClose = true;
2290
+ return path;
2291
+ }
2292
+
2293
+ const RE = /([+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)(?:,|\s)([+-]?\d*\.?\d+(?:e[+-]?\d+)?)/g;
2294
+ function parsePolylineNode(node) {
2295
+ const path = new Path2D();
2296
+ let index = 0;
2297
+ node.getAttribute("points")?.replace(RE, (match, a, b) => {
2298
+ const x = parseFloatWithUnits(a);
2299
+ const y = parseFloatWithUnits(b);
2300
+ if (index === 0) {
2301
+ path.moveTo(x, y);
2302
+ } else {
2303
+ path.lineTo(x, y);
2304
+ }
2305
+ index++;
2306
+ return match;
2307
+ });
2308
+ path.currentCurve.autoClose = false;
2309
+ return path;
2310
+ }
2311
+
2312
+ function parseRectNode(node) {
2313
+ const x = parseFloatWithUnits(node.getAttribute("x") || 0);
2314
+ const y = parseFloatWithUnits(node.getAttribute("y") || 0);
2315
+ const rx = parseFloatWithUnits(node.getAttribute("rx") || node.getAttribute("ry") || 0);
2316
+ const ry = parseFloatWithUnits(node.getAttribute("ry") || node.getAttribute("rx") || 0);
2317
+ const w = parseFloatWithUnits(node.getAttribute("width"));
2318
+ const h = parseFloatWithUnits(node.getAttribute("height"));
2319
+ const bci = 1 - 0.551915024494;
2320
+ const path = new Path2D();
2321
+ path.moveTo(x + rx, y);
2322
+ path.lineTo(x + w - rx, y);
2323
+ if (rx !== 0 || ry !== 0) {
2324
+ path.bezierCurveTo(
2325
+ x + w - rx * bci,
2326
+ y,
2327
+ x + w,
2328
+ y + ry * bci,
2329
+ x + w,
2330
+ y + ry
2331
+ );
2332
+ }
2333
+ path.lineTo(x + w, y + h - ry);
2334
+ if (rx !== 0 || ry !== 0) {
2335
+ path.bezierCurveTo(
2336
+ x + w,
2337
+ y + h - ry * bci,
2338
+ x + w - rx * bci,
2339
+ y + h,
2340
+ x + w - rx,
2341
+ y + h
2342
+ );
2343
+ }
2344
+ path.lineTo(x + rx, y + h);
2345
+ if (rx !== 0 || ry !== 0) {
2346
+ path.bezierCurveTo(
2347
+ x + rx * bci,
2348
+ y + h,
2349
+ x,
2350
+ y + h - ry * bci,
2351
+ x,
2352
+ y + h - ry
2353
+ );
2354
+ }
2355
+ path.lineTo(x, y + ry);
2356
+ if (rx !== 0 || ry !== 0) {
2357
+ path.bezierCurveTo(x, y + ry * bci, x + rx * bci, y, x + rx, y);
2358
+ }
2359
+ return path;
2360
+ }
2361
+
2362
+ function parseStyle(node, style, stylesheets) {
2363
+ style = Object.assign({}, style);
2364
+ let stylesheetStyles = {};
2365
+ if (node.hasAttribute("class")) {
2366
+ const classSelectors = node.getAttribute("class").split(/\s/).filter(Boolean).map((i) => i.trim());
2367
+ for (let i = 0; i < classSelectors.length; i++) {
2368
+ stylesheetStyles = Object.assign(stylesheetStyles, stylesheets[`.${classSelectors[i]}`]);
2328
2369
  }
2329
2370
  }
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
- }
2371
+ if (node.hasAttribute("id")) {
2372
+ stylesheetStyles = Object.assign(stylesheetStyles, stylesheets[`#${node.getAttribute("id")}`]);
2358
2373
  }
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);
2374
+ for (let len = node.style.length, i = 0; i < len; i++) {
2375
+ const name = node.style.item(i);
2376
+ const value = node.style.getPropertyValue(name);
2377
+ style[name] = value;
2378
+ stylesheetStyles[name] = value;
2371
2379
  }
2372
- return {
2373
- vertices,
2374
- indices
2375
- };
2376
- }
2377
- function getOrientationOfPoints(points) {
2378
- const m = points.length;
2379
- if (m < 6) {
2380
- return 1;
2380
+ function addStyle(svgName, jsName, adjustFunction = copy) {
2381
+ if (node.hasAttribute(svgName))
2382
+ style[jsName] = adjustFunction(node.getAttribute(svgName));
2383
+ if (stylesheetStyles[svgName])
2384
+ style[jsName] = adjustFunction(stylesheetStyles[svgName]);
2381
2385
  }
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;
2386
+ function copy(v) {
2387
+ if (v.startsWith("url"))
2388
+ console.warn("url access in attributes is not implemented.");
2389
+ return v;
2389
2390
  }
2390
- if (area < 0) {
2391
- return -1;
2391
+ function clamp(v) {
2392
+ return Math.max(0, Math.min(1, parseFloatWithUnits(v)));
2392
2393
  }
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;
2394
+ function positive(v) {
2395
+ return Math.max(0, parseFloatWithUnits(v));
2408
2396
  }
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;
2397
+ function array(v) {
2398
+ return v.split(" ").filter((v2) => v2 !== "").map((v2) => parseFloatWithUnits(v2));
2426
2399
  }
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);
2400
+ addStyle("fill", "fill");
2401
+ addStyle("fill-opacity", "fillOpacity", clamp);
2402
+ addStyle("fill-rule", "fillRule");
2403
+ addStyle("opacity", "opacity", clamp);
2404
+ addStyle("stroke", "stroke");
2405
+ addStyle("stroke-opacity", "strokeOpacity", clamp);
2406
+ addStyle("stroke-width", "strokeWidth", positive);
2407
+ addStyle("stroke-linecap", "strokeLinecap");
2408
+ addStyle("stroke-linejoin", "strokeLinejoin");
2409
+ addStyle("stroke-miterlimit", "strokeMiterlimit", positive);
2410
+ addStyle("stroke-dasharray", "strokeDasharray", array);
2411
+ addStyle("stroke-dashoffset", "strokeDashoffset", parseFloatWithUnits);
2412
+ addStyle("visibility", "visibility");
2413
+ return style;
2414
+ }
2415
+
2416
+ function parseNode(node, style, paths = [], stylesheets = {}) {
2417
+ if (node.nodeType !== 1)
2418
+ return paths;
2419
+ let isDefsNode = false;
2420
+ let path = null;
2421
+ let _style = { ...style };
2422
+ switch (node.nodeName) {
2423
+ case "svg":
2424
+ _style = parseStyle(node, _style, stylesheets);
2425
+ break;
2426
+ case "style":
2427
+ parseCSSStylesheet(node, stylesheets);
2428
+ break;
2429
+ case "g":
2430
+ _style = parseStyle(node, _style, stylesheets);
2431
+ break;
2432
+ case "path":
2433
+ _style = parseStyle(node, _style, stylesheets);
2434
+ if (node.hasAttribute("d"))
2435
+ path = parsePathNode(node);
2436
+ break;
2437
+ case "rect":
2438
+ _style = parseStyle(node, _style, stylesheets);
2439
+ path = parseRectNode(node);
2440
+ break;
2441
+ case "polygon":
2442
+ _style = parseStyle(node, _style, stylesheets);
2443
+ path = parsePolygonNode(node);
2444
+ break;
2445
+ case "polyline":
2446
+ _style = parseStyle(node, _style, stylesheets);
2447
+ path = parsePolylineNode(node);
2448
+ break;
2449
+ case "circle":
2450
+ _style = parseStyle(node, _style, stylesheets);
2451
+ path = parseCircleNode(node);
2452
+ break;
2453
+ case "ellipse":
2454
+ _style = parseStyle(node, _style, stylesheets);
2455
+ path = parseEllipseNode(node);
2456
+ break;
2457
+ case "line":
2458
+ _style = parseStyle(node, _style, stylesheets);
2459
+ path = parseLineNode(node);
2460
+ break;
2461
+ case "defs":
2462
+ isDefsNode = true;
2463
+ break;
2464
+ case "use": {
2465
+ _style = parseStyle(node, _style, stylesheets);
2466
+ const href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href") || node.getAttribute("href") || "";
2467
+ const usedNodeId = href.substring(1);
2468
+ const usedNode = node.viewportElement?.getElementById(usedNodeId);
2469
+ if (usedNode) {
2470
+ parseNode(usedNode, _style, paths, stylesheets);
2471
+ } else {
2472
+ console.warn(`'use node' references non-existent node id: ${usedNodeId}`);
2473
+ }
2474
+ break;
2440
2475
  }
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);
2476
+ default:
2477
+ console.warn(node);
2478
+ break;
2479
+ }
2480
+ if (_style.display === "none") {
2481
+ return paths;
2482
+ }
2483
+ const currentTransform = new Transform2D();
2484
+ const transformStack = [];
2485
+ const transform = getNodeTransform(node, currentTransform, transformStack);
2486
+ if (path) {
2487
+ path.applyTransform(currentTransform);
2488
+ paths.push(path);
2489
+ path.style = { ..._style };
2490
+ }
2491
+ const childNodes = node.childNodes;
2492
+ for (let i = 0, len = childNodes.length; i < len; i++) {
2493
+ const node2 = childNodes[i];
2494
+ if (isDefsNode && node2.nodeName !== "style" && node2.nodeName !== "defs")
2495
+ continue;
2496
+ parseNode(node2, _style, paths, stylesheets);
2497
+ }
2498
+ if (transform) {
2499
+ transformStack.pop();
2500
+ if (transformStack.length > 0) {
2501
+ currentTransform.copyFrom(transformStack[transformStack.length - 1]);
2502
+ } else {
2503
+ currentTransform.identity();
2449
2504
  }
2450
- verts.push(ex, ey);
2451
- verts.push(cx, cy);
2452
2505
  }
2453
- return segCount * 2;
2506
+ return paths;
2507
+ }
2508
+
2509
+ function svgToPath2DSet(svg) {
2510
+ const dom = svgToDom(svg);
2511
+ return new Path2DSet(
2512
+ parseNode(dom, {}),
2513
+ dom.getAttribute("viewBox")?.trim().split(" ").map((v) => Number(v))
2514
+ );
2454
2515
  }
2455
2516
 
2456
2517
  class Curve {
@@ -4480,6 +4541,9 @@ exports.getDirectedArea = getDirectedArea;
4480
4541
  exports.getIntersectionPoint = getIntersectionPoint;
4481
4542
  exports.nonzeroFillRule = nonzeroFillRule;
4482
4543
  exports.parseArcCommand = parseArcCommand;
4544
+ exports.parseCssArg = parseCssArg;
4545
+ exports.parseCssArgs = parseCssArgs;
4546
+ exports.parseCssFunctions = parseCssFunctions;
4483
4547
  exports.parsePathDataArgs = parsePathDataArgs;
4484
4548
  exports.quadraticBezier = quadraticBezier;
4485
4549
  exports.setCanvasContext = setCanvasContext;