modern-path2d 0.1.9 → 0.1.11

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
@@ -1,5 +1,33 @@
1
1
  'use strict';
2
2
 
3
+ const lineJoinMap = {
4
+ "arcs": "bevel",
5
+ "bevel": "bevel",
6
+ "miter": "miter",
7
+ "miter-clip": "miter",
8
+ "round": "round"
9
+ };
10
+ function setCanvasContextByPath(ctx, path) {
11
+ const {
12
+ fill = "#000",
13
+ stroke = "none",
14
+ strokeWidth = stroke === "none" ? 0 : 1,
15
+ strokeLinecap = "round",
16
+ strokeLinejoin = "miter",
17
+ strokeMiterlimit = 0,
18
+ strokeDasharray,
19
+ strokeDashoffset = 0
20
+ } = path.style;
21
+ ctx.fillStyle = fill;
22
+ ctx.strokeStyle = stroke;
23
+ ctx.lineWidth = strokeWidth;
24
+ ctx.lineCap = strokeLinecap;
25
+ ctx.lineJoin = lineJoinMap[strokeLinejoin];
26
+ ctx.miterLimit = strokeMiterlimit;
27
+ ctx.lineDashOffset = strokeDashoffset;
28
+ strokeDasharray && ctx.setLineDash(strokeDasharray);
29
+ }
30
+
3
31
  class Vector2 {
4
32
  constructor(x = 0, y = 0) {
5
33
  this.x = x;
@@ -384,221 +412,175 @@ function parseArcCommand(path, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, s
384
412
  const cy = Math.sin(xAxisRotation) * cxp + Math.cos(xAxisRotation) * cyp + (start.y + end.y) / 2;
385
413
  const theta = svgAngle(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);
386
414
  const delta = svgAngle((x1p - cxp) / rx, (y1p - cyp) / ry, (-x1p - cxp) / rx, (-y1p - cyp) / ry) % (Math.PI * 2);
387
- path.currentPath.absellipse(cx, cy, rx, ry, theta, theta + delta, sweepFlag === 0, xAxisRotation);
415
+ path.ellipse(cx, cy, rx, ry, xAxisRotation, theta, theta + delta, sweepFlag === 1);
388
416
  }
389
417
 
390
418
  function getReflection(a, b) {
391
419
  return a - (b - a);
392
420
  }
393
421
  function addPathCommandsToPath2D(commands, path) {
394
- const point = new Vector2();
422
+ const current = new Vector2();
395
423
  const control = new Vector2();
396
- const firstPoint = new Vector2();
397
- let isFirstPoint = true;
398
- let doSetFirstPoint = false;
399
424
  for (let i = 0, l = commands.length; i < l; i++) {
400
- const command = commands[i];
401
- if (isFirstPoint) {
402
- doSetFirstPoint = true;
403
- isFirstPoint = false;
404
- }
405
- if (command.type === "m" || command.type === "M") {
406
- if (command.type === "m") {
407
- point.x += command.x;
408
- point.y += command.y;
425
+ const cmd = commands[i];
426
+ if (cmd.type === "m" || cmd.type === "M") {
427
+ if (cmd.type === "m") {
428
+ current.add(cmd);
409
429
  } else {
410
- point.x = command.x;
411
- point.y = command.y;
430
+ current.copy(cmd);
412
431
  }
413
- control.x = point.x;
414
- control.y = point.y;
415
- path.moveTo(point.x, point.y);
416
- firstPoint.copy(point);
417
- } else if (command.type === "h" || command.type === "H") {
418
- if (command.type === "h") {
419
- point.x += command.x;
432
+ path.moveTo(current.x, current.y);
433
+ control.copy(current);
434
+ } else if (cmd.type === "h" || cmd.type === "H") {
435
+ if (cmd.type === "h") {
436
+ current.x += cmd.x;
420
437
  } else {
421
- point.x = command.x;
438
+ current.x = cmd.x;
422
439
  }
423
- control.x = point.x;
424
- control.y = point.y;
425
- path.lineTo(point.x, point.y);
426
- if (doSetFirstPoint)
427
- firstPoint.copy(point);
428
- } else if (command.type === "v" || command.type === "V") {
429
- if (command.type === "v") {
430
- point.y += command.y;
440
+ path.lineTo(current.x, current.y);
441
+ control.copy(current);
442
+ } else if (cmd.type === "v" || cmd.type === "V") {
443
+ if (cmd.type === "v") {
444
+ current.y += cmd.y;
431
445
  } else {
432
- point.y = command.y;
446
+ current.y = cmd.y;
433
447
  }
434
- control.x = point.x;
435
- control.y = point.y;
436
- path.lineTo(point.x, point.y);
437
- if (doSetFirstPoint)
438
- firstPoint.copy(point);
439
- } else if (command.type === "l" || command.type === "L") {
440
- if (command.type === "l") {
441
- point.x += command.x;
442
- point.y += command.y;
448
+ path.lineTo(current.x, current.y);
449
+ control.copy(current);
450
+ } else if (cmd.type === "l" || cmd.type === "L") {
451
+ if (cmd.type === "l") {
452
+ current.add(cmd);
443
453
  } else {
444
- point.x = command.x;
445
- point.y = command.y;
454
+ current.copy(cmd);
446
455
  }
447
- control.x = point.x;
448
- control.y = point.y;
449
- path.lineTo(point.x, point.y);
450
- if (doSetFirstPoint)
451
- firstPoint.copy(point);
452
- } else if (command.type === "c" || command.type === "C") {
453
- if (command.type === "c") {
456
+ path.lineTo(current.x, current.y);
457
+ control.copy(current);
458
+ } else if (cmd.type === "c" || cmd.type === "C") {
459
+ if (cmd.type === "c") {
454
460
  path.bezierCurveTo(
455
- point.x + command.x1,
456
- point.y + command.y1,
457
- point.x + command.x2,
458
- point.y + command.y2,
459
- point.x + command.x,
460
- point.y + command.y
461
+ current.x + cmd.x1,
462
+ current.y + cmd.y1,
463
+ current.x + cmd.x2,
464
+ current.y + cmd.y2,
465
+ current.x + cmd.x,
466
+ current.y + cmd.y
461
467
  );
462
- control.x = point.x + command.x2;
463
- control.y = point.y + command.y2;
464
- point.x += command.x;
465
- point.y += command.y;
468
+ control.x = current.x + cmd.x2;
469
+ control.y = current.y + cmd.y2;
470
+ current.add(cmd);
466
471
  } else {
467
472
  path.bezierCurveTo(
468
- command.x1,
469
- command.y1,
470
- command.x2,
471
- command.y2,
472
- command.x,
473
- command.y
473
+ cmd.x1,
474
+ cmd.y1,
475
+ cmd.x2,
476
+ cmd.y2,
477
+ cmd.x,
478
+ cmd.y
474
479
  );
475
- control.x = command.x2;
476
- control.y = command.y2;
477
- point.x = command.x;
478
- point.y = command.y;
480
+ control.x = cmd.x2;
481
+ control.y = cmd.y2;
482
+ current.copy(cmd);
479
483
  }
480
- if (doSetFirstPoint)
481
- firstPoint.copy(point);
482
- } else if (command.type === "s" || command.type === "S") {
483
- if (command.type === "s") {
484
+ } else if (cmd.type === "s" || cmd.type === "S") {
485
+ if (cmd.type === "s") {
484
486
  path.bezierCurveTo(
485
- getReflection(point.x, control.x),
486
- getReflection(point.y, control.y),
487
- point.x + command.x2,
488
- point.y + command.y2,
489
- point.x + command.x,
490
- point.y + command.y
487
+ getReflection(current.x, control.x),
488
+ getReflection(current.y, control.y),
489
+ current.x + cmd.x2,
490
+ current.y + cmd.y2,
491
+ current.x + cmd.x,
492
+ current.y + cmd.y
491
493
  );
492
- control.x = point.x + command.x2;
493
- control.y = point.y + command.y2;
494
- point.x += command.x;
495
- point.y += command.y;
494
+ control.x = current.x + cmd.x2;
495
+ control.y = current.y + cmd.y2;
496
+ current.add(cmd);
496
497
  } else {
497
498
  path.bezierCurveTo(
498
- getReflection(point.x, control.x),
499
- getReflection(point.y, control.y),
500
- command.x2,
501
- command.y2,
502
- command.x,
503
- command.y
499
+ getReflection(current.x, control.x),
500
+ getReflection(current.y, control.y),
501
+ cmd.x2,
502
+ cmd.y2,
503
+ cmd.x,
504
+ cmd.y
504
505
  );
505
- control.x = command.x2;
506
- control.y = command.y2;
507
- point.x = command.x;
508
- point.y = command.y;
506
+ control.x = cmd.x2;
507
+ control.y = cmd.y2;
508
+ current.copy(cmd);
509
509
  }
510
- if (doSetFirstPoint)
511
- firstPoint.copy(point);
512
- } else if (command.type === "q" || command.type === "Q") {
513
- if (command.type === "q") {
510
+ } else if (cmd.type === "q" || cmd.type === "Q") {
511
+ if (cmd.type === "q") {
514
512
  path.quadraticCurveTo(
515
- point.x + command.x1,
516
- point.y + command.y1,
517
- point.x + command.x,
518
- point.y + command.y
513
+ current.x + cmd.x1,
514
+ current.y + cmd.y1,
515
+ current.x + cmd.x,
516
+ current.y + cmd.y
519
517
  );
520
- control.x = point.x + command.x1;
521
- control.y = point.y + command.y1;
522
- point.x += command.x;
523
- point.y += command.y;
518
+ control.x = current.x + cmd.x1;
519
+ control.y = current.y + cmd.y1;
520
+ current.add(cmd);
524
521
  } else {
525
522
  path.quadraticCurveTo(
526
- command.x1,
527
- command.y1,
528
- command.x,
529
- command.y
523
+ cmd.x1,
524
+ cmd.y1,
525
+ cmd.x,
526
+ cmd.y
530
527
  );
531
- control.x = command.x1;
532
- control.y = command.y1;
533
- point.x = command.x;
534
- point.y = command.y;
528
+ control.x = cmd.x1;
529
+ control.y = cmd.y1;
530
+ current.copy(cmd);
535
531
  }
536
- if (doSetFirstPoint)
537
- firstPoint.copy(point);
538
- } else if (command.type === "t" || command.type === "T") {
539
- const rx = getReflection(point.x, control.x);
540
- const ry = getReflection(point.y, control.y);
532
+ } else if (cmd.type === "t" || cmd.type === "T") {
533
+ const rx = getReflection(current.x, control.x);
534
+ const ry = getReflection(current.y, control.y);
541
535
  control.x = rx;
542
536
  control.y = ry;
543
- if (command.type === "t") {
537
+ if (cmd.type === "t") {
544
538
  path.quadraticCurveTo(
545
539
  rx,
546
540
  ry,
547
- point.x + command.x,
548
- point.y + command.y
541
+ current.x + cmd.x,
542
+ current.y + cmd.y
549
543
  );
550
- point.x += command.x;
551
- point.y += command.y;
544
+ current.add(cmd);
552
545
  } else {
553
546
  path.quadraticCurveTo(
554
547
  rx,
555
548
  ry,
556
- command.x,
557
- command.y
549
+ cmd.x,
550
+ cmd.y
558
551
  );
559
- point.x = command.x;
560
- point.y = command.y;
552
+ current.copy(cmd);
561
553
  }
562
- if (doSetFirstPoint)
563
- firstPoint.copy(point);
564
- } else if (command.type === "a" || command.type === "A") {
565
- if (command.type === "a") {
566
- if (command.x === 0 && command.y === 0)
554
+ } else if (cmd.type === "a" || cmd.type === "A") {
555
+ const start = current.clone();
556
+ if (cmd.type === "a") {
557
+ if (cmd.x === 0 && cmd.y === 0)
567
558
  continue;
568
- point.x += command.x;
569
- point.y += command.y;
559
+ current.add(cmd);
570
560
  } else {
571
- if (command.x === point.x && command.y === point.y)
561
+ if (current.equals(cmd))
572
562
  continue;
573
- point.x = command.x;
574
- point.y = command.y;
563
+ current.copy(cmd);
575
564
  }
576
- const start = point.clone();
577
- control.x = point.x;
578
- control.y = point.y;
565
+ control.copy(current);
579
566
  parseArcCommand(
580
567
  path,
581
- command.rx,
582
- command.ry,
583
- command.angle,
584
- command.largeArcFlag,
585
- command.sweepFlag,
568
+ cmd.rx,
569
+ cmd.ry,
570
+ cmd.angle,
571
+ cmd.largeArcFlag,
572
+ cmd.sweepFlag,
586
573
  start,
587
- point
574
+ current
588
575
  );
589
- if (doSetFirstPoint)
590
- firstPoint.copy(point);
591
- } else if (command.type === "z" || command.type === "Z") {
592
- path.currentPath.autoClose = true;
593
- if (path.currentPath.curves.length > 0) {
594
- point.copy(firstPoint);
595
- path.currentPath.currentPoint.copy(point);
596
- isFirstPoint = true;
576
+ } else if (cmd.type === "z" || cmd.type === "Z") {
577
+ if (path.startPoint) {
578
+ current.copy(path.startPoint);
597
579
  }
580
+ path.closePath();
598
581
  } else {
599
- console.warn("Unsupported commands", command);
582
+ console.warn("Unsupported commands", cmd);
600
583
  }
601
- doSetFirstPoint = false;
602
584
  }
603
585
  }
604
586
 
@@ -1189,15 +1171,15 @@ const tempTransform1$1 = new Matrix3();
1189
1171
  const tempTransform2$1 = new Matrix3();
1190
1172
  const tempV2 = new Vector2();
1191
1173
  class EllipseCurve extends Curve {
1192
- constructor(center = new Vector2(), radiusX = 1, radiusY = 1, startAngle = 0, endAngle = Math.PI * 2, clockwise = false, rotation = 0) {
1174
+ constructor(center = new Vector2(), radiusX = 1, radiusY = 1, rotation = 0, startAngle = 0, endAngle = Math.PI * 2, clockwise = false) {
1193
1175
  super();
1194
1176
  this.center = center;
1195
1177
  this.radiusX = radiusX;
1196
1178
  this.radiusY = radiusY;
1179
+ this.rotation = rotation;
1197
1180
  this.startAngle = startAngle;
1198
1181
  this.endAngle = endAngle;
1199
1182
  this.clockwise = clockwise;
1200
- this.rotation = rotation;
1201
1183
  }
1202
1184
  getPoint(t, output = new Vector2()) {
1203
1185
  const twoPi = Math.PI * 2;
@@ -1881,27 +1863,36 @@ class CurvePath extends Curve {
1881
1863
  constructor(points) {
1882
1864
  super();
1883
1865
  __publicField$1(this, "curves", []);
1866
+ __publicField$1(this, "startPoint");
1884
1867
  __publicField$1(this, "currentPoint", new Vector2());
1885
1868
  __publicField$1(this, "autoClose", false);
1886
1869
  __publicField$1(this, "_cacheLengths", []);
1887
1870
  if (points) {
1888
- this.setFromPoints(points);
1871
+ this.addPoints(points);
1889
1872
  }
1890
1873
  }
1891
1874
  addCurve(curve) {
1892
1875
  this.curves.push(curve);
1893
1876
  return this;
1894
1877
  }
1895
- closePath() {
1896
- const start = this.curves[0].getPoint(0);
1897
- const end = this.curves[this.curves.length - 1].getPoint(1);
1898
- if (!start.equals(end)) {
1899
- this.curves.push(new LineCurve(end, start));
1878
+ addPoints(points) {
1879
+ this.moveTo(points[0].x, points[0].y);
1880
+ for (let i = 1, len = points.length; i < len; i++) {
1881
+ const { x, y } = points[i];
1882
+ this.lineTo(x, y);
1900
1883
  }
1901
1884
  return this;
1902
1885
  }
1903
- getPoint(position, output = new Vector2()) {
1904
- const d = position * this.getLength();
1886
+ addCommands(commands) {
1887
+ addPathCommandsToPath2D(commands, this);
1888
+ return this;
1889
+ }
1890
+ addData(data) {
1891
+ this.addCommands(pathDataToPathCommands(data));
1892
+ return this;
1893
+ }
1894
+ getPoint(t, output = new Vector2()) {
1895
+ const d = t * this.getLength();
1905
1896
  const curveLengths = this.getCurveLengths();
1906
1897
  let i = 0;
1907
1898
  while (i < curveLengths.length) {
@@ -1966,23 +1957,28 @@ class CurvePath extends Curve {
1966
1957
  }
1967
1958
  return points;
1968
1959
  }
1969
- setFromPoints(points) {
1970
- this.moveTo(points[0].x, points[0].y);
1971
- for (let i = 1, l = points.length; i < l; i++) {
1972
- this.lineTo(points[i].x, points[i].y);
1960
+ _setCurrentPoint(point) {
1961
+ this.currentPoint.copy(point);
1962
+ if (!this.startPoint) {
1963
+ this.startPoint = this.currentPoint.clone();
1973
1964
  }
1974
1965
  return this;
1975
1966
  }
1976
- bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
1977
- this.curves.push(
1978
- new CubicBezierCurve(
1979
- this.currentPoint.clone(),
1980
- new Vector2(cp1x, cp1y),
1981
- new Vector2(cp2x, cp2y),
1982
- new Vector2(x, y)
1983
- )
1984
- );
1967
+ closePath() {
1968
+ const start = this.startPoint;
1969
+ if (start) {
1970
+ const end = this.currentPoint;
1971
+ if (!start.equals(end)) {
1972
+ this.curves.push(new LineCurve(end, start));
1973
+ this.currentPoint.copy(start);
1974
+ }
1975
+ this.startPoint = void 0;
1976
+ }
1977
+ return this;
1978
+ }
1979
+ moveTo(x, y) {
1985
1980
  this.currentPoint.set(x, y);
1981
+ this.startPoint = this.currentPoint.clone();
1986
1982
  return this;
1987
1983
  }
1988
1984
  lineTo(x, y) {
@@ -1992,64 +1988,56 @@ class CurvePath extends Curve {
1992
1988
  new Vector2(x, y)
1993
1989
  )
1994
1990
  );
1995
- this.currentPoint.set(x, y);
1996
- return this;
1997
- }
1998
- moveTo(x, y) {
1999
- this.currentPoint.set(x, y);
1991
+ this._setCurrentPoint({ x, y });
2000
1992
  return this;
2001
1993
  }
2002
- quadraticCurveTo(cpx, cpy, x, y) {
1994
+ bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
2003
1995
  this.curves.push(
2004
- new QuadraticBezierCurve(
1996
+ new CubicBezierCurve(
2005
1997
  this.currentPoint.clone(),
2006
- new Vector2(cpx, cpy),
1998
+ new Vector2(cp1x, cp1y),
1999
+ new Vector2(cp2x, cp2y),
2007
2000
  new Vector2(x, y)
2008
2001
  )
2009
2002
  );
2010
- this.currentPoint.set(x, y);
2003
+ this._setCurrentPoint({ x, y });
2011
2004
  return this;
2012
2005
  }
2013
- rect(x, y, w, h) {
2006
+ quadraticCurveTo(cpx, cpy, x, y) {
2014
2007
  this.curves.push(
2015
- new RectangularCurve(
2016
- new Vector2(x + w / 2, y + h / 2),
2017
- w / 2,
2018
- w / h
2008
+ new QuadraticBezierCurve(
2009
+ this.currentPoint.clone(),
2010
+ new Vector2(cpx, cpy),
2011
+ new Vector2(x, y)
2019
2012
  )
2020
2013
  );
2021
- this.currentPoint.set(x, y);
2014
+ this._setCurrentPoint({ x, y });
2022
2015
  return this;
2023
2016
  }
2024
- splineThru(points) {
2025
- const npts = [this.currentPoint.clone()].concat(points);
2026
- this.curves.push(new SplineCurve(npts));
2027
- this.currentPoint.copy(points[points.length - 1]);
2017
+ arc(x, y, radius, startAngle, endAngle, counterclockwise) {
2018
+ this.ellipse(x, y, radius, radius, 0, startAngle, endAngle, counterclockwise);
2028
2019
  return this;
2029
2020
  }
2030
- arc(x, y, radius, startAngle, endAngle, clockwise = false) {
2021
+ relativeArc(x, y, radius, startAngle, endAngle, counterclockwise) {
2031
2022
  const point = this.currentPoint;
2032
- this.absarc(x + point.x, y + point.y, radius, startAngle, endAngle, clockwise);
2033
- return this;
2034
- }
2035
- absarc(x, y, radius, startAngle, endAngle, clockwise = false) {
2036
- this.absellipse(x, y, radius, radius, startAngle, endAngle, clockwise);
2023
+ this.arc(x + point.x, y + point.y, radius, startAngle, endAngle, counterclockwise);
2037
2024
  return this;
2038
2025
  }
2039
- ellipse(x, y, radiusX, radiusY, startAngle, endAngle, clockwise = false, rotation = 0) {
2040
- const point = this.currentPoint;
2041
- this.absellipse(x + point.x, y + point.y, radiusX, radiusY, startAngle, endAngle, clockwise, rotation);
2026
+ // TODO
2027
+ // eslint-disable-next-line unused-imports/no-unused-vars
2028
+ arcTo(x1, y1, x2, y2, radius) {
2029
+ console.warn("Method arcTo not supported yet");
2042
2030
  return this;
2043
2031
  }
2044
- absellipse(x, y, radiusX, radiusY, startAngle, endAngle, clockwise = false, rotation = 0) {
2032
+ ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise = true) {
2045
2033
  const curve = new EllipseCurve(
2046
2034
  new Vector2(x, y),
2047
2035
  radiusX,
2048
2036
  radiusY,
2037
+ rotation,
2049
2038
  startAngle,
2050
2039
  endAngle,
2051
- clockwise,
2052
- rotation
2040
+ !counterclockwise
2053
2041
  );
2054
2042
  if (this.curves.length > 0) {
2055
2043
  const first = curve.getPoint(0);
@@ -2058,16 +2046,45 @@ class CurvePath extends Curve {
2058
2046
  }
2059
2047
  }
2060
2048
  this.curves.push(curve);
2061
- this.currentPoint.copy(curve.getPoint(1));
2049
+ this._setCurrentPoint(curve.getPoint(1));
2062
2050
  return this;
2063
2051
  }
2064
- getCommands() {
2065
- return this.curves.flatMap((curve) => curve.getCommands());
2052
+ relativeEllipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) {
2053
+ const point = this.currentPoint;
2054
+ this.ellipse(x + point.x, y + point.y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise);
2055
+ return this;
2056
+ }
2057
+ rect(x, y, w, h) {
2058
+ this.curves.push(
2059
+ new RectangularCurve(
2060
+ new Vector2(x + w / 2, y + h / 2),
2061
+ w / 2,
2062
+ w / h
2063
+ )
2064
+ );
2065
+ this._setCurrentPoint({ x, y });
2066
+ return this;
2067
+ }
2068
+ splineThru(points) {
2069
+ this.curves.push(new SplineCurve([this.currentPoint.clone()].concat(points)));
2070
+ this._setCurrentPoint(points[points.length - 1]);
2071
+ return this;
2072
+ }
2073
+ transformPoint(cb) {
2074
+ this.curves.forEach((curve) => curve.transformPoint(cb));
2075
+ return this;
2066
2076
  }
2067
2077
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
2068
2078
  this.curves.forEach((curve) => curve.getMinMax(min, max));
2069
2079
  return { min, max };
2070
2080
  }
2081
+ getBoundingBox() {
2082
+ const { min, max } = this.getMinMax();
2083
+ return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
2084
+ }
2085
+ getCommands() {
2086
+ return this.curves.flatMap((curve) => curve.getCommands());
2087
+ }
2071
2088
  drawTo(ctx) {
2072
2089
  const point = this.curves[0]?.getPoint(0);
2073
2090
  if (point) {
@@ -2091,6 +2108,10 @@ class CurvePath extends Curve {
2091
2108
  }
2092
2109
  }
2093
2110
 
2111
+ function toKebabCase(str) {
2112
+ return str.replace(/[^a-z0-9]/gi, "-").replace(/\B([A-Z])/g, "-$1").toLowerCase();
2113
+ }
2114
+
2094
2115
  var __defProp = Object.defineProperty;
2095
2116
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
2096
2117
  var __publicField = (obj, key, value) => {
@@ -2101,7 +2122,7 @@ class Path2D {
2101
2122
  constructor(path) {
2102
2123
  __publicField(this, "currentPath", new CurvePath());
2103
2124
  __publicField(this, "paths", [this.currentPath]);
2104
- __publicField(this, "userData");
2125
+ __publicField(this, "style", {});
2105
2126
  if (path) {
2106
2127
  if (path instanceof Path2D) {
2107
2128
  this.addPath(path);
@@ -2112,6 +2133,15 @@ class Path2D {
2112
2133
  }
2113
2134
  }
2114
2135
  }
2136
+ get startPoint() {
2137
+ return this.currentPath.startPoint;
2138
+ }
2139
+ get currentPoint() {
2140
+ return this.currentPath.currentPoint;
2141
+ }
2142
+ get strokeWidth() {
2143
+ return this.style.strokeWidth ?? ((this.style.stroke ?? "none") === "none" ? 0 : 1);
2144
+ }
2115
2145
  addPath(path) {
2116
2146
  if (path instanceof Path2D) {
2117
2147
  this.paths.push(...path.paths.map((v) => v.clone()));
@@ -2121,12 +2151,19 @@ class Path2D {
2121
2151
  return this;
2122
2152
  }
2123
2153
  closePath() {
2124
- this.currentPath.closePath();
2154
+ const startPoint = this.startPoint;
2155
+ if (startPoint) {
2156
+ this.currentPath.closePath();
2157
+ if (this.currentPath.curves.length > 0) {
2158
+ this.currentPath = new CurvePath().moveTo(startPoint.x, startPoint.y);
2159
+ this.paths.push(this.currentPath);
2160
+ }
2161
+ }
2125
2162
  return this;
2126
2163
  }
2127
2164
  moveTo(x, y) {
2128
2165
  const { currentPoint, curves } = this.currentPath;
2129
- if (currentPoint.x !== x || currentPoint.y !== y) {
2166
+ if (!currentPoint.equals({ x, y })) {
2130
2167
  if (curves.length) {
2131
2168
  this.currentPath = new CurvePath().moveTo(x, y);
2132
2169
  this.paths.push(this.currentPath);
@@ -2149,17 +2186,15 @@ class Path2D {
2149
2186
  return this;
2150
2187
  }
2151
2188
  arc(x, y, radius, startAngle, endAngle, counterclockwise) {
2152
- this.currentPath.absarc(x, y, radius, startAngle, endAngle, !counterclockwise);
2189
+ this.currentPath.arc(x, y, radius, startAngle, endAngle, counterclockwise);
2153
2190
  return this;
2154
2191
  }
2155
- // TODO
2156
- // eslint-disable-next-line unused-imports/no-unused-vars
2157
2192
  arcTo(x1, y1, x2, y2, radius) {
2158
- console.warn("Method arcTo not supported yet");
2193
+ this.currentPath.arcTo(x1, y1, x2, y2, radius);
2159
2194
  return this;
2160
2195
  }
2161
2196
  ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) {
2162
- this.currentPath.absellipse(x, y, radiusX, radiusY, startAngle, endAngle, !counterclockwise, rotation);
2197
+ this.currentPath.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise);
2163
2198
  return this;
2164
2199
  }
2165
2200
  rect(x, y, w, h) {
@@ -2194,56 +2229,92 @@ class Path2D {
2194
2229
  this.forEachCurve((curve) => curve.getMinMax(min, max));
2195
2230
  return { min, max };
2196
2231
  }
2197
- getBoundingBox() {
2232
+ getBoundingBox(withStrokeWidth = true) {
2198
2233
  const { min, max } = this.getMinMax();
2199
- return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
2234
+ const bbox = new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
2235
+ if (withStrokeWidth) {
2236
+ const strokeWidth = this.strokeWidth;
2237
+ bbox.left -= strokeWidth / 2;
2238
+ bbox.top -= strokeWidth / 2;
2239
+ bbox.width += strokeWidth;
2240
+ bbox.height += strokeWidth;
2241
+ }
2242
+ return bbox;
2200
2243
  }
2201
2244
  getCommands() {
2202
- return this.paths.flatMap((path) => path.curves.flatMap((curve) => curve.getCommands()));
2245
+ return this.paths.flatMap((path) => path.getCommands());
2203
2246
  }
2204
2247
  getData() {
2205
2248
  return this.paths.map((path) => path.getData()).join(" ");
2206
2249
  }
2207
- getSvgString() {
2250
+ getSvgPathXml() {
2251
+ const style = {
2252
+ ...this.style,
2253
+ fill: this.style.fill ?? "#000",
2254
+ stroke: this.style.stroke ?? "none"
2255
+ };
2256
+ const cssStyle = {};
2257
+ for (const key in style) {
2258
+ if (style[key] !== void 0) {
2259
+ cssStyle[toKebabCase(key)] = style[key];
2260
+ }
2261
+ }
2262
+ Object.assign(cssStyle, {
2263
+ "stroke-width": `${this.strokeWidth}px`
2264
+ });
2265
+ let cssText = "";
2266
+ for (const key in cssStyle) {
2267
+ if (cssStyle[key] !== void 0) {
2268
+ cssText += `${key}:${cssStyle[key]};`;
2269
+ }
2270
+ }
2271
+ return `<path d="${this.getData()}" style="${cssText}"></path>`;
2272
+ }
2273
+ getSvgXml() {
2208
2274
  const { x, y, width, height } = this.getBoundingBox();
2209
- const strokeWidth = 1;
2210
- return `<svg viewBox="${x - strokeWidth} ${y - strokeWidth} ${width + strokeWidth * 2} ${height + strokeWidth * 2}" xmlns="http://www.w3.org/2000/svg"><path fill="none" stroke="currentColor" d="${this.getData()}"></path></svg>`;
2275
+ const path = this.getSvgPathXml();
2276
+ return `<svg viewBox="${x} ${y} ${width} ${height}" width="${width}px" height="${height}px" xmlns="http://www.w3.org/2000/svg">${path}</svg>`;
2211
2277
  }
2212
2278
  getSvgDataUri() {
2213
- return `data:image/svg+xml;base64,${btoa(this.getSvgString())}`;
2279
+ return `data:image/svg+xml;base64,${btoa(this.getSvgXml())}`;
2214
2280
  }
2215
2281
  drawTo(ctx) {
2282
+ const {
2283
+ fill = "#000",
2284
+ stroke = "none"
2285
+ } = this.style;
2286
+ setCanvasContextByPath(ctx, this);
2216
2287
  this.paths.forEach((path) => {
2217
2288
  path.drawTo(ctx);
2218
2289
  });
2219
- }
2220
- strokeTo(ctx) {
2221
- this.drawTo(ctx);
2222
- ctx.stroke();
2223
- }
2224
- fillTo(ctx) {
2225
- this.drawTo(ctx);
2226
- ctx.fill();
2290
+ if (fill !== "none") {
2291
+ ctx.fill();
2292
+ }
2293
+ if (stroke !== "none") {
2294
+ ctx.stroke();
2295
+ }
2227
2296
  }
2228
2297
  copy(source) {
2229
2298
  this.currentPath = source.currentPath.clone();
2230
2299
  this.paths = source.paths.map((path) => path.clone());
2231
- this.userData = source.userData;
2300
+ this.style = { ...source.style };
2232
2301
  return this;
2233
2302
  }
2234
- toCanvas(fill = true) {
2235
- const canvas = document.createElement("canvas");
2303
+ toSvg() {
2304
+ return new DOMParser().parseFromString(this.getSvgXml(), "image/svg+xml").documentElement;
2305
+ }
2306
+ toCanvas(pixelRatio = 2) {
2236
2307
  const { left, top, width, height } = this.getBoundingBox();
2237
- canvas.width = width;
2238
- canvas.height = height;
2308
+ const canvas = document.createElement("canvas");
2309
+ canvas.width = width * pixelRatio;
2310
+ canvas.height = height * pixelRatio;
2311
+ canvas.style.width = `${width}px`;
2312
+ canvas.style.height = `${height}px`;
2239
2313
  const ctx = canvas.getContext("2d");
2240
2314
  if (ctx) {
2315
+ ctx.scale(pixelRatio, pixelRatio);
2241
2316
  ctx.translate(-left, -top);
2242
- if (fill) {
2243
- this.fillTo(ctx);
2244
- } else {
2245
- this.strokeTo(ctx);
2246
- }
2317
+ this.drawTo(ctx);
2247
2318
  }
2248
2319
  return canvas;
2249
2320
  }
@@ -2452,7 +2523,7 @@ function parseNodeTransform(node) {
2452
2523
 
2453
2524
  function parseCircleNode(node) {
2454
2525
  return new Path2D().addPath(
2455
- new CurvePath().absarc(
2526
+ new CurvePath().arc(
2456
2527
  parseFloatWithUnits(node.getAttribute("cx") || 0),
2457
2528
  parseFloatWithUnits(node.getAttribute("cy") || 0),
2458
2529
  parseFloatWithUnits(node.getAttribute("r") || 0),
@@ -2484,12 +2555,13 @@ function parseCSSStylesheet(node, stylesheets) {
2484
2555
 
2485
2556
  function parseEllipseNode(node) {
2486
2557
  return new Path2D().addPath(
2487
- new CurvePath().absellipse(
2558
+ new CurvePath().ellipse(
2488
2559
  parseFloatWithUnits(node.getAttribute("cx") || 0),
2489
2560
  parseFloatWithUnits(node.getAttribute("cy") || 0),
2490
2561
  parseFloatWithUnits(node.getAttribute("rx") || 0),
2491
2562
  parseFloatWithUnits(node.getAttribute("ry") || 0),
2492
2563
  0,
2564
+ 0,
2493
2565
  Math.PI * 2
2494
2566
  )
2495
2567
  );
@@ -2635,18 +2707,21 @@ function parseStyle(node, style, stylesheets) {
2635
2707
  function positive(v) {
2636
2708
  return Math.max(0, parseFloatWithUnits(v));
2637
2709
  }
2710
+ function array(v) {
2711
+ return v.split(" ").filter((v2) => v2 !== "").map((v2) => parseFloatWithUnits(v2));
2712
+ }
2638
2713
  addStyle("fill", "fill");
2639
2714
  addStyle("fill-opacity", "fillOpacity", clamp);
2640
2715
  addStyle("fill-rule", "fillRule");
2641
2716
  addStyle("opacity", "opacity", clamp);
2642
2717
  addStyle("stroke", "stroke");
2643
- addStyle("stroke-dashoffset", "strokeDashoffset");
2644
- addStyle("stroke-dasharray", "strokeDasharray");
2645
- addStyle("stroke-linecap", "strokeLineCap");
2646
- addStyle("stroke-linejoin", "strokeLineJoin");
2647
- addStyle("stroke-miterlimit", "strokeMiterLimit", positive);
2648
2718
  addStyle("stroke-opacity", "strokeOpacity", clamp);
2649
2719
  addStyle("stroke-width", "strokeWidth", positive);
2720
+ addStyle("stroke-linecap", "strokeLinecap");
2721
+ addStyle("stroke-linejoin", "strokeLinejoin");
2722
+ addStyle("stroke-miterlimit", "strokeMiterlimit", positive);
2723
+ addStyle("stroke-dasharray", "strokeDasharray", array);
2724
+ addStyle("stroke-dashoffset", "strokeDashoffset", parseFloatWithUnits);
2650
2725
  addStyle("visibility", "visibility");
2651
2726
  return style;
2652
2727
  }
@@ -2721,7 +2796,7 @@ function parseNode(node, style, paths = []) {
2721
2796
  if (path) {
2722
2797
  path.transform(currentTransform);
2723
2798
  paths.push(path);
2724
- path.userData = { node, style };
2799
+ path.style = style;
2725
2800
  }
2726
2801
  const childNodes = node.childNodes;
2727
2802
  for (let i = 0, len = childNodes.length; i < len; i++) {
@@ -2765,15 +2840,7 @@ function parseSvgToDom(svg) {
2765
2840
  }
2766
2841
  }
2767
2842
  function parseSvg(svg) {
2768
- return parseNode(parseSvgToDom(svg), {
2769
- fill: "#000",
2770
- fillOpacity: 1,
2771
- strokeOpacity: 1,
2772
- strokeWidth: 1,
2773
- strokeLineJoin: "miter",
2774
- strokeLineCap: "butt",
2775
- strokeMiterLimit: 4
2776
- });
2843
+ return parseNode(parseSvgToDom(svg), {});
2777
2844
  }
2778
2845
 
2779
2846
  exports.BoundingBox = BoundingBox;
@@ -2791,5 +2858,11 @@ exports.QuadraticBezierCurve = QuadraticBezierCurve;
2791
2858
  exports.RectangularCurve = RectangularCurve;
2792
2859
  exports.SplineCurve = SplineCurve;
2793
2860
  exports.Vector2 = Vector2;
2861
+ exports.addPathCommandsToPath2D = addPathCommandsToPath2D;
2862
+ exports.parseArcCommand = parseArcCommand;
2863
+ exports.parsePathDataArgs = parsePathDataArgs;
2794
2864
  exports.parseSvg = parseSvg;
2795
2865
  exports.parseSvgToDom = parseSvgToDom;
2866
+ exports.pathCommandsToPathData = pathCommandsToPathData;
2867
+ exports.pathDataToPathCommands = pathDataToPathCommands;
2868
+ exports.setCanvasContextByPath = setCanvasContextByPath;