modern-path2d 1.0.1 → 1.2.0

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,17 @@
1
1
  'use strict';
2
2
 
3
+ const earcut = require('earcut');
4
+
5
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
6
+
7
+ const earcut__default = /*#__PURE__*/_interopDefaultCompat(earcut);
8
+
9
+ function drawPoint(ctx, x, y, options = {}) {
10
+ const { radius = 1 } = options;
11
+ ctx.moveTo(x, y);
12
+ ctx.arc(x, y, radius, 0, Math.PI * 2);
13
+ }
14
+
3
15
  const lineJoinMap = {
4
16
  "arcs": "bevel",
5
17
  "bevel": "bevel",
@@ -494,10 +506,145 @@ function parseArcCommand(path, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, s
494
506
  path.ellipse(cx, cy, rx, ry, xAxisRotation, theta, theta + delta, sweepFlag === 0);
495
507
  }
496
508
 
509
+ const RE$3 = {
510
+ SEPARATOR: /[ \t\r\n,.\-+]/,
511
+ WHITESPACE: /[ \t\r\n]/,
512
+ DIGIT: /\d/,
513
+ SIGN: /[-+]/,
514
+ POINT: /\./,
515
+ COMMA: /,/,
516
+ EXP: /e/i,
517
+ FLAGS: /[01]/
518
+ };
519
+ function parsePathDataArgs(input, flags, stride = 0) {
520
+ const SEP = 0;
521
+ const INT = 1;
522
+ const FLOAT = 2;
523
+ const EXP = 3;
524
+ let state = SEP;
525
+ let seenComma = true;
526
+ let number = "";
527
+ let exponent = "";
528
+ const result = [];
529
+ function throwSyntaxError(current2, i, partial) {
530
+ const error = new SyntaxError(`Unexpected character "${current2}" at index ${i}.`);
531
+ error.partial = partial;
532
+ throw error;
533
+ }
534
+ function newNumber() {
535
+ if (number !== "") {
536
+ if (exponent === "")
537
+ result.push(Number(number));
538
+ else result.push(Number(number) * 10 ** Number(exponent));
539
+ }
540
+ number = "";
541
+ exponent = "";
542
+ }
543
+ let current;
544
+ const length = input.length;
545
+ for (let i = 0; i < length; i++) {
546
+ current = input[i];
547
+ if (Array.isArray(flags) && flags.includes(result.length % stride) && RE$3.FLAGS.test(current)) {
548
+ state = INT;
549
+ number = current;
550
+ newNumber();
551
+ continue;
552
+ }
553
+ if (state === SEP) {
554
+ if (RE$3.WHITESPACE.test(current)) {
555
+ continue;
556
+ }
557
+ if (RE$3.DIGIT.test(current) || RE$3.SIGN.test(current)) {
558
+ state = INT;
559
+ number = current;
560
+ continue;
561
+ }
562
+ if (RE$3.POINT.test(current)) {
563
+ state = FLOAT;
564
+ number = current;
565
+ continue;
566
+ }
567
+ if (RE$3.COMMA.test(current)) {
568
+ if (seenComma) {
569
+ throwSyntaxError(current, i, result);
570
+ }
571
+ seenComma = true;
572
+ }
573
+ }
574
+ if (state === INT) {
575
+ if (RE$3.DIGIT.test(current)) {
576
+ number += current;
577
+ continue;
578
+ }
579
+ if (RE$3.POINT.test(current)) {
580
+ number += current;
581
+ state = FLOAT;
582
+ continue;
583
+ }
584
+ if (RE$3.EXP.test(current)) {
585
+ state = EXP;
586
+ continue;
587
+ }
588
+ if (RE$3.SIGN.test(current) && number.length === 1 && RE$3.SIGN.test(number[0])) {
589
+ throwSyntaxError(current, i, result);
590
+ }
591
+ }
592
+ if (state === FLOAT) {
593
+ if (RE$3.DIGIT.test(current)) {
594
+ number += current;
595
+ continue;
596
+ }
597
+ if (RE$3.EXP.test(current)) {
598
+ state = EXP;
599
+ continue;
600
+ }
601
+ if (RE$3.POINT.test(current) && number[number.length - 1] === ".") {
602
+ throwSyntaxError(current, i, result);
603
+ }
604
+ }
605
+ if (state === EXP) {
606
+ if (RE$3.DIGIT.test(current)) {
607
+ exponent += current;
608
+ continue;
609
+ }
610
+ if (RE$3.SIGN.test(current)) {
611
+ if (exponent === "") {
612
+ exponent += current;
613
+ continue;
614
+ }
615
+ if (exponent.length === 1 && RE$3.SIGN.test(exponent)) {
616
+ throwSyntaxError(current, i, result);
617
+ }
618
+ }
619
+ }
620
+ if (RE$3.WHITESPACE.test(current)) {
621
+ newNumber();
622
+ state = SEP;
623
+ seenComma = false;
624
+ } else if (RE$3.COMMA.test(current)) {
625
+ newNumber();
626
+ state = SEP;
627
+ seenComma = true;
628
+ } else if (RE$3.SIGN.test(current)) {
629
+ newNumber();
630
+ state = INT;
631
+ number = current;
632
+ } else if (RE$3.POINT.test(current)) {
633
+ newNumber();
634
+ state = FLOAT;
635
+ number = current;
636
+ } else {
637
+ throwSyntaxError(current, i, result);
638
+ }
639
+ }
640
+ newNumber();
641
+ return result;
642
+ }
643
+
497
644
  function getReflection(a, b) {
498
645
  return a - (b - a);
499
646
  }
500
- function addPathCommandsToPath2D(commands, path) {
647
+ function svgPathCommandsAddToPath2D(commands, path) {
501
648
  const current = new Vector2();
502
649
  const control = new Vector2();
503
650
  for (let i = 0, l = commands.length; i < l; i++) {
@@ -663,151 +810,16 @@ function addPathCommandsToPath2D(commands, path) {
663
810
  }
664
811
  }
665
812
 
666
- const RE$3 = {
667
- SEPARATOR: /[ \t\r\n,.\-+]/,
668
- WHITESPACE: /[ \t\r\n]/,
669
- DIGIT: /\d/,
670
- SIGN: /[-+]/,
671
- POINT: /\./,
672
- COMMA: /,/,
673
- EXP: /e/i,
674
- FLAGS: /[01]/
675
- };
676
- function parsePathDataArgs(input, flags, stride = 0) {
677
- const SEP = 0;
678
- const INT = 1;
679
- const FLOAT = 2;
680
- const EXP = 3;
681
- let state = SEP;
682
- let seenComma = true;
683
- let number = "";
684
- let exponent = "";
685
- const result = [];
686
- function throwSyntaxError(current2, i, partial) {
687
- const error = new SyntaxError(`Unexpected character "${current2}" at index ${i}.`);
688
- error.partial = partial;
689
- throw error;
690
- }
691
- function newNumber() {
692
- if (number !== "") {
693
- if (exponent === "")
694
- result.push(Number(number));
695
- else result.push(Number(number) * 10 ** Number(exponent));
696
- }
697
- number = "";
698
- exponent = "";
699
- }
700
- let current;
701
- const length = input.length;
702
- for (let i = 0; i < length; i++) {
703
- current = input[i];
704
- if (Array.isArray(flags) && flags.includes(result.length % stride) && RE$3.FLAGS.test(current)) {
705
- state = INT;
706
- number = current;
707
- newNumber();
708
- continue;
709
- }
710
- if (state === SEP) {
711
- if (RE$3.WHITESPACE.test(current)) {
712
- continue;
713
- }
714
- if (RE$3.DIGIT.test(current) || RE$3.SIGN.test(current)) {
715
- state = INT;
716
- number = current;
717
- continue;
718
- }
719
- if (RE$3.POINT.test(current)) {
720
- state = FLOAT;
721
- number = current;
722
- continue;
723
- }
724
- if (RE$3.COMMA.test(current)) {
725
- if (seenComma) {
726
- throwSyntaxError(current, i, result);
727
- }
728
- seenComma = true;
729
- }
730
- }
731
- if (state === INT) {
732
- if (RE$3.DIGIT.test(current)) {
733
- number += current;
734
- continue;
735
- }
736
- if (RE$3.POINT.test(current)) {
737
- number += current;
738
- state = FLOAT;
739
- continue;
740
- }
741
- if (RE$3.EXP.test(current)) {
742
- state = EXP;
743
- continue;
744
- }
745
- if (RE$3.SIGN.test(current) && number.length === 1 && RE$3.SIGN.test(number[0])) {
746
- throwSyntaxError(current, i, result);
747
- }
748
- }
749
- if (state === FLOAT) {
750
- if (RE$3.DIGIT.test(current)) {
751
- number += current;
752
- continue;
753
- }
754
- if (RE$3.EXP.test(current)) {
755
- state = EXP;
756
- continue;
757
- }
758
- if (RE$3.POINT.test(current) && number[number.length - 1] === ".") {
759
- throwSyntaxError(current, i, result);
760
- }
761
- }
762
- if (state === EXP) {
763
- if (RE$3.DIGIT.test(current)) {
764
- exponent += current;
765
- continue;
766
- }
767
- if (RE$3.SIGN.test(current)) {
768
- if (exponent === "") {
769
- exponent += current;
770
- continue;
771
- }
772
- if (exponent.length === 1 && RE$3.SIGN.test(exponent)) {
773
- throwSyntaxError(current, i, result);
774
- }
775
- }
776
- }
777
- if (RE$3.WHITESPACE.test(current)) {
778
- newNumber();
779
- state = SEP;
780
- seenComma = false;
781
- } else if (RE$3.COMMA.test(current)) {
782
- newNumber();
783
- state = SEP;
784
- seenComma = true;
785
- } else if (RE$3.SIGN.test(current)) {
786
- newNumber();
787
- state = INT;
788
- number = current;
789
- } else if (RE$3.POINT.test(current)) {
790
- newNumber();
791
- state = FLOAT;
792
- number = current;
793
- } else {
794
- throwSyntaxError(current, i, result);
795
- }
796
- }
797
- newNumber();
798
- return result;
799
- }
800
-
801
- function pathCommandsToPathData(commands) {
802
- let first;
803
- let prev;
804
- const data = [];
805
- for (let i = 0, len = commands.length; i < len; i++) {
806
- const cmd = commands[i];
807
- switch (cmd.type) {
808
- case "m":
809
- case "M":
810
- if (cmd.x.toFixed(4) === prev?.x.toFixed(4) && cmd.y.toFixed(4) === prev?.y.toFixed(4)) {
813
+ function svgPathCommandsToData(commands) {
814
+ let first;
815
+ let prev;
816
+ const data = [];
817
+ for (let i = 0, len = commands.length; i < len; i++) {
818
+ const cmd = commands[i];
819
+ switch (cmd.type) {
820
+ case "m":
821
+ case "M":
822
+ if (cmd.x.toFixed(4) === prev?.x.toFixed(4) && cmd.y.toFixed(4) === prev?.y.toFixed(4)) {
811
823
  continue;
812
824
  }
813
825
  data.push(`${cmd.type} ${cmd.x} ${cmd.y}`);
@@ -867,21 +879,21 @@ function pathCommandsToPathData(commands) {
867
879
  }
868
880
 
869
881
  const RE$2 = /[a-df-z][^a-df-z]*/gi;
870
- function pathDataToPathCommands(d) {
882
+ function svgPathDataToCommands(data) {
871
883
  const commands = [];
872
- const matched = d.match(RE$2);
884
+ const matched = data.match(RE$2);
873
885
  if (!matched) {
874
886
  return commands;
875
887
  }
876
888
  for (let i = 0, len = matched.length; i < len; i++) {
877
889
  const command = matched[i];
878
890
  const type = command.charAt(0);
879
- const data = command.slice(1).trim();
891
+ const data2 = command.slice(1).trim();
880
892
  let args;
881
893
  switch (type) {
882
894
  case "m":
883
895
  case "M":
884
- args = parsePathDataArgs(data);
896
+ args = parsePathDataArgs(data2);
885
897
  for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 2) {
886
898
  if (i2 === 0) {
887
899
  commands.push({ type, x: args[i2], y: args[i2 + 1] });
@@ -892,28 +904,28 @@ function pathDataToPathCommands(d) {
892
904
  break;
893
905
  case "h":
894
906
  case "H":
895
- args = parsePathDataArgs(data);
907
+ args = parsePathDataArgs(data2);
896
908
  for (let i2 = 0, len2 = args.length; i2 < len2; i2++) {
897
909
  commands.push({ type, x: args[i2] });
898
910
  }
899
911
  break;
900
912
  case "v":
901
913
  case "V":
902
- args = parsePathDataArgs(data);
914
+ args = parsePathDataArgs(data2);
903
915
  for (let i2 = 0, len2 = args.length; i2 < len2; i2++) {
904
916
  commands.push({ type, y: args[i2] });
905
917
  }
906
918
  break;
907
919
  case "l":
908
920
  case "L":
909
- args = parsePathDataArgs(data);
921
+ args = parsePathDataArgs(data2);
910
922
  for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 2) {
911
923
  commands.push({ type, x: args[i2], y: args[i2 + 1] });
912
924
  }
913
925
  break;
914
926
  case "c":
915
927
  case "C":
916
- args = parsePathDataArgs(data);
928
+ args = parsePathDataArgs(data2);
917
929
  for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 6) {
918
930
  commands.push({
919
931
  type,
@@ -928,7 +940,7 @@ function pathDataToPathCommands(d) {
928
940
  break;
929
941
  case "s":
930
942
  case "S":
931
- args = parsePathDataArgs(data);
943
+ args = parsePathDataArgs(data2);
932
944
  for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 4) {
933
945
  commands.push({
934
946
  type,
@@ -941,7 +953,7 @@ function pathDataToPathCommands(d) {
941
953
  break;
942
954
  case "q":
943
955
  case "Q":
944
- args = parsePathDataArgs(data);
956
+ args = parsePathDataArgs(data2);
945
957
  for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 4) {
946
958
  commands.push({
947
959
  type,
@@ -954,7 +966,7 @@ function pathDataToPathCommands(d) {
954
966
  break;
955
967
  case "t":
956
968
  case "T":
957
- args = parsePathDataArgs(data);
969
+ args = parsePathDataArgs(data2);
958
970
  for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 2) {
959
971
  commands.push({
960
972
  type,
@@ -965,7 +977,7 @@ function pathDataToPathCommands(d) {
965
977
  break;
966
978
  case "a":
967
979
  case "A":
968
- args = parsePathDataArgs(data, [3, 4], 7);
980
+ args = parsePathDataArgs(data2, [3, 4], 7);
969
981
  for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 7) {
970
982
  commands.push({
971
983
  type,
@@ -992,80 +1004,690 @@ function pathDataToPathCommands(d) {
992
1004
  return commands;
993
1005
  }
994
1006
 
1007
+ function catmullRom(t, p0, p1, p2, p3) {
1008
+ const v0 = (p2 - p0) * 0.5;
1009
+ const v1 = (p3 - p1) * 0.5;
1010
+ const t2 = t * t;
1011
+ const t3 = t * t2;
1012
+ return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1;
1013
+ }
1014
+
1015
+ function cubicBezierP0(t, p) {
1016
+ const k = 1 - t;
1017
+ return k * k * k * p;
1018
+ }
1019
+ function cubicBezierP1(t, p) {
1020
+ const k = 1 - t;
1021
+ return 3 * k * k * t * p;
1022
+ }
1023
+ function cubicBezierP2(t, p) {
1024
+ return 3 * (1 - t) * t * t * p;
1025
+ }
1026
+ function cubicBezierP3(t, p) {
1027
+ return t * t * t * p;
1028
+ }
1029
+ function cubicBezier(t, p0, p1, p2, p3) {
1030
+ return cubicBezierP0(t, p0) + cubicBezierP1(t, p1) + cubicBezierP2(t, p2) + cubicBezierP3(t, p3);
1031
+ }
1032
+
1033
+ function fillTriangulate(points, options = {}) {
1034
+ let {
1035
+ vertices = [],
1036
+ indices = [],
1037
+ holes = [],
1038
+ verticesStride = 2,
1039
+ verticesOffset = 0,
1040
+ indicesOffset = 0
1041
+ } = options;
1042
+ const triangles = earcut__default(points, holes, 2);
1043
+ if (triangles) {
1044
+ for (let i = 0; i < triangles.length; i += 3) {
1045
+ indices[indicesOffset++] = triangles[i] + verticesOffset;
1046
+ indices[indicesOffset++] = triangles[i + 1] + verticesOffset;
1047
+ indices[indicesOffset++] = triangles[i + 2] + verticesOffset;
1048
+ }
1049
+ let index = verticesOffset * verticesStride;
1050
+ for (let i = 0; i < points.length; i += 2) {
1051
+ vertices[index] = points[i];
1052
+ vertices[index + 1] = points[i + 1];
1053
+ index += verticesStride;
1054
+ }
1055
+ }
1056
+ return {
1057
+ vertices,
1058
+ indices
1059
+ };
1060
+ }
1061
+
1062
+ const RECURSION_LIMIT$1 = 8;
1063
+ const FLT_EPSILON$1 = 11920929e-14;
1064
+ const PATH_DISTANCE_EPSILON$1 = 1;
1065
+ function getAdaptiveCubicBezierCurvePoints(sX, sY, x1, y1, x2, y2, x, y, smoothness = 0.5, points) {
1066
+ const scale = 1;
1067
+ const smoothing = Math.min(
1068
+ 0.99,
1069
+ // a value of 1.0 actually inverts smoothing, so we cap it at 0.99
1070
+ Math.max(0, smoothness)
1071
+ );
1072
+ let distanceTolerance = (PATH_DISTANCE_EPSILON$1 - smoothing) / scale;
1073
+ distanceTolerance *= distanceTolerance;
1074
+ recursive$1(sX, sY, x1, y1, x2, y2, x, y, points, distanceTolerance, 0);
1075
+ points.push(x, y);
1076
+ return points;
1077
+ }
1078
+ function recursive$1(x1, y1, x2, y2, x3, y3, x4, y4, points, distanceTolerance, level) {
1079
+ if (level > RECURSION_LIMIT$1)
1080
+ return;
1081
+ const x12 = (x1 + x2) / 2;
1082
+ const y12 = (y1 + y2) / 2;
1083
+ const x23 = (x2 + x3) / 2;
1084
+ const y23 = (y2 + y3) / 2;
1085
+ const x34 = (x3 + x4) / 2;
1086
+ const y34 = (y3 + y4) / 2;
1087
+ const x123 = (x12 + x23) / 2;
1088
+ const y123 = (y12 + y23) / 2;
1089
+ const x234 = (x23 + x34) / 2;
1090
+ const y234 = (y23 + y34) / 2;
1091
+ const x1234 = (x123 + x234) / 2;
1092
+ const y1234 = (y123 + y234) / 2;
1093
+ if (level > 0) {
1094
+ let dx = x4 - x1;
1095
+ let dy = y4 - y1;
1096
+ const d2 = Math.abs((x2 - x4) * dy - (y2 - y4) * dx);
1097
+ const d3 = Math.abs((x3 - x4) * dy - (y3 - y4) * dx);
1098
+ if (d2 > FLT_EPSILON$1 && d3 > FLT_EPSILON$1) {
1099
+ if ((d2 + d3) * (d2 + d3) <= distanceTolerance * (dx * dx + dy * dy)) {
1100
+ {
1101
+ points.push(x1234, y1234);
1102
+ return;
1103
+ }
1104
+ }
1105
+ } else if (d2 > FLT_EPSILON$1) {
1106
+ if (d2 * d2 <= distanceTolerance * (dx * dx + dy * dy)) {
1107
+ {
1108
+ points.push(x1234, y1234);
1109
+ return;
1110
+ }
1111
+ }
1112
+ } else if (d3 > FLT_EPSILON$1) {
1113
+ if (d3 * d3 <= distanceTolerance * (dx * dx + dy * dy)) {
1114
+ {
1115
+ points.push(x1234, y1234);
1116
+ return;
1117
+ }
1118
+ }
1119
+ } else {
1120
+ dx = x1234 - (x1 + x4) / 2;
1121
+ dy = y1234 - (y1 + y4) / 2;
1122
+ if (dx * dx + dy * dy <= distanceTolerance) {
1123
+ points.push(x1234, y1234);
1124
+ return;
1125
+ }
1126
+ }
1127
+ }
1128
+ recursive$1(x1, y1, x12, y12, x123, y123, x1234, y1234, points, distanceTolerance, level + 1);
1129
+ recursive$1(x1234, y1234, x234, y234, x34, y34, x4, y4, points, distanceTolerance, level + 1);
1130
+ }
1131
+
1132
+ const RECURSION_LIMIT = 8;
1133
+ const FLT_EPSILON = 11920929e-14;
1134
+ const PATH_DISTANCE_EPSILON = 1;
1135
+ function getAdaptiveQuadraticBezierCurvePoints(sX, sY, x1, y1, x, y, smoothness = 0.5, points) {
1136
+ const scale = 1;
1137
+ const smoothing = Math.min(
1138
+ 0.99,
1139
+ // a value of 1.0 actually inverts smoothing, so we cap it at 0.99
1140
+ Math.max(0, smoothness)
1141
+ );
1142
+ let distanceTolerance = (PATH_DISTANCE_EPSILON - smoothing) / scale;
1143
+ distanceTolerance *= distanceTolerance;
1144
+ recursive(points, sX, sY, x1, y1, x, y, distanceTolerance, 0);
1145
+ points.push(x, y);
1146
+ return points;
1147
+ }
1148
+ function recursive(points, x1, y1, x2, y2, x3, y3, distanceTolerance, level) {
1149
+ if (level > RECURSION_LIMIT)
1150
+ return;
1151
+ const x12 = (x1 + x2) / 2;
1152
+ const y12 = (y1 + y2) / 2;
1153
+ const x23 = (x2 + x3) / 2;
1154
+ const y23 = (y2 + y3) / 2;
1155
+ const x123 = (x12 + x23) / 2;
1156
+ const y123 = (y12 + y23) / 2;
1157
+ let dx = x3 - x1;
1158
+ let dy = y3 - y1;
1159
+ const d = Math.abs((x2 - x3) * dy - (y2 - y3) * dx);
1160
+ if (d > FLT_EPSILON) {
1161
+ if (d * d <= distanceTolerance * (dx * dx + dy * dy)) {
1162
+ {
1163
+ points.push(x123, y123);
1164
+ return;
1165
+ }
1166
+ }
1167
+ } else {
1168
+ dx = x123 - (x1 + x3) / 2;
1169
+ dy = y123 - (y1 + y3) / 2;
1170
+ if (dx * dx + dy * dy <= distanceTolerance) {
1171
+ points.push(x123, y123);
1172
+ return;
1173
+ }
1174
+ }
1175
+ recursive(points, x1, y1, x12, y12, x123, y123, distanceTolerance, level + 1);
1176
+ recursive(points, x123, y123, x23, y23, x3, y3, distanceTolerance, level + 1);
1177
+ }
1178
+
1179
+ function quadraticBezierP0(t, p) {
1180
+ const k = 1 - t;
1181
+ return k * k * p;
1182
+ }
1183
+ function quadraticBezierP1(t, p) {
1184
+ return 2 * (1 - t) * t * p;
1185
+ }
1186
+ function quadraticBezierP2(t, p) {
1187
+ return t * t * p;
1188
+ }
1189
+ function quadraticBezier(t, p0, p1, p2) {
1190
+ return quadraticBezierP0(t, p0) + quadraticBezierP1(t, p1) + quadraticBezierP2(t, p2);
1191
+ }
1192
+
1193
+ const closePointEps = 1e-4;
1194
+ const curveEps = 1e-4;
1195
+ function strokeTriangulate(points, options = {}) {
1196
+ const {
1197
+ vertices = [],
1198
+ indices = [],
1199
+ lineStyle = {
1200
+ alignment: 0.5,
1201
+ cap: "butt",
1202
+ join: "miter",
1203
+ width: 1,
1204
+ miterLimit: 10
1205
+ },
1206
+ flipAlignment = false,
1207
+ closed = true
1208
+ } = options;
1209
+ const eps = closePointEps;
1210
+ if (points.length === 0) {
1211
+ return { vertices, indices };
1212
+ }
1213
+ const style = lineStyle;
1214
+ let alignment = style.alignment;
1215
+ if (lineStyle.alignment !== 0.5) {
1216
+ let orientation = getOrientationOfPoints(points);
1217
+ if (flipAlignment)
1218
+ orientation *= -1;
1219
+ alignment = (alignment - 0.5) * orientation + 0.5;
1220
+ }
1221
+ const firstPoint = { x: points[0], y: points[1] };
1222
+ const lastPoint = { x: points[points.length - 2], y: points[points.length - 1] };
1223
+ const closedShape = closed;
1224
+ const closedPath = Math.abs(firstPoint.x - lastPoint.x) < eps && Math.abs(firstPoint.y - lastPoint.y) < eps;
1225
+ if (closedShape) {
1226
+ points = points.slice();
1227
+ if (closedPath) {
1228
+ points.pop();
1229
+ points.pop();
1230
+ lastPoint.x = points[points.length - 2];
1231
+ lastPoint.y = points[points.length - 1];
1232
+ }
1233
+ const midPointX = (firstPoint.x + lastPoint.x) * 0.5;
1234
+ const midPointY = (lastPoint.y + firstPoint.y) * 0.5;
1235
+ points.unshift(midPointX, midPointY);
1236
+ points.push(midPointX, midPointY);
1237
+ }
1238
+ const verts = vertices;
1239
+ const length = points.length / 2;
1240
+ let indexCount = points.length;
1241
+ const indexStart = verts.length / 2;
1242
+ const width = style.width / 2;
1243
+ const widthSquared = width * width;
1244
+ const miterLimitSquared = style.miterLimit * style.miterLimit;
1245
+ let x0 = points[0];
1246
+ let y0 = points[1];
1247
+ let x1 = points[2];
1248
+ let y1 = points[3];
1249
+ let x2 = 0;
1250
+ let y2 = 0;
1251
+ let perpX = -(y0 - y1);
1252
+ let perpY = x0 - x1;
1253
+ let perp1x = 0;
1254
+ let perp1y = 0;
1255
+ let dist = Math.sqrt(perpX * perpX + perpY * perpY);
1256
+ perpX /= dist;
1257
+ perpY /= dist;
1258
+ perpX *= width;
1259
+ perpY *= width;
1260
+ const ratio = alignment;
1261
+ const innerWeight = (1 - ratio) * 2;
1262
+ const outerWeight = ratio * 2;
1263
+ if (!closedShape) {
1264
+ if (style.cap === "round") {
1265
+ indexCount += round(
1266
+ x0 - perpX * (innerWeight - outerWeight) * 0.5,
1267
+ y0 - perpY * (innerWeight - outerWeight) * 0.5,
1268
+ x0 - perpX * innerWeight,
1269
+ y0 - perpY * innerWeight,
1270
+ x0 + perpX * outerWeight,
1271
+ y0 + perpY * outerWeight,
1272
+ verts,
1273
+ true
1274
+ ) + 2;
1275
+ } else if (style.cap === "square") {
1276
+ indexCount += square(x0, y0, perpX, perpY, innerWeight, outerWeight, true, verts);
1277
+ }
1278
+ }
1279
+ verts.push(
1280
+ x0 - perpX * innerWeight,
1281
+ y0 - perpY * innerWeight
1282
+ );
1283
+ verts.push(
1284
+ x0 + perpX * outerWeight,
1285
+ y0 + perpY * outerWeight
1286
+ );
1287
+ for (let i = 1; i < length - 1; ++i) {
1288
+ x0 = points[(i - 1) * 2];
1289
+ y0 = points[(i - 1) * 2 + 1];
1290
+ x1 = points[i * 2];
1291
+ y1 = points[i * 2 + 1];
1292
+ x2 = points[(i + 1) * 2];
1293
+ y2 = points[(i + 1) * 2 + 1];
1294
+ perpX = -(y0 - y1);
1295
+ perpY = x0 - x1;
1296
+ dist = Math.sqrt(perpX * perpX + perpY * perpY);
1297
+ perpX /= dist;
1298
+ perpY /= dist;
1299
+ perpX *= width;
1300
+ perpY *= width;
1301
+ perp1x = -(y1 - y2);
1302
+ perp1y = x1 - x2;
1303
+ dist = Math.sqrt(perp1x * perp1x + perp1y * perp1y);
1304
+ perp1x /= dist;
1305
+ perp1y /= dist;
1306
+ perp1x *= width;
1307
+ perp1y *= width;
1308
+ const dx0 = x1 - x0;
1309
+ const dy0 = y0 - y1;
1310
+ const dx1 = x1 - x2;
1311
+ const dy1 = y2 - y1;
1312
+ const dot = dx0 * dx1 + dy0 * dy1;
1313
+ const cross = dy0 * dx1 - dy1 * dx0;
1314
+ const clockwise = cross < 0;
1315
+ if (Math.abs(cross) < 1e-3 * Math.abs(dot)) {
1316
+ verts.push(
1317
+ x1 - perpX * innerWeight,
1318
+ y1 - perpY * innerWeight
1319
+ );
1320
+ verts.push(
1321
+ x1 + perpX * outerWeight,
1322
+ y1 + perpY * outerWeight
1323
+ );
1324
+ if (dot >= 0) {
1325
+ if (style.join === "round") {
1326
+ indexCount += round(
1327
+ x1,
1328
+ y1,
1329
+ x1 - perpX * innerWeight,
1330
+ y1 - perpY * innerWeight,
1331
+ x1 - perp1x * innerWeight,
1332
+ y1 - perp1y * innerWeight,
1333
+ verts,
1334
+ false
1335
+ ) + 4;
1336
+ } else {
1337
+ indexCount += 2;
1338
+ }
1339
+ verts.push(
1340
+ x1 - perp1x * outerWeight,
1341
+ y1 - perp1y * outerWeight
1342
+ );
1343
+ verts.push(
1344
+ x1 + perp1x * innerWeight,
1345
+ y1 + perp1y * innerWeight
1346
+ );
1347
+ }
1348
+ continue;
1349
+ }
1350
+ const c1 = (-perpX + x0) * (-perpY + y1) - (-perpX + x1) * (-perpY + y0);
1351
+ const c2 = (-perp1x + x2) * (-perp1y + y1) - (-perp1x + x1) * (-perp1y + y2);
1352
+ const px = (dx0 * c2 - dx1 * c1) / cross;
1353
+ const py = (dy1 * c1 - dy0 * c2) / cross;
1354
+ const pDist = (px - x1) * (px - x1) + (py - y1) * (py - y1);
1355
+ const imx = x1 + (px - x1) * innerWeight;
1356
+ const imy = y1 + (py - y1) * innerWeight;
1357
+ const omx = x1 - (px - x1) * outerWeight;
1358
+ const omy = y1 - (py - y1) * outerWeight;
1359
+ const smallerInsideSegmentSq = Math.min(dx0 * dx0 + dy0 * dy0, dx1 * dx1 + dy1 * dy1);
1360
+ const insideWeight = clockwise ? innerWeight : outerWeight;
1361
+ const smallerInsideDiagonalSq = smallerInsideSegmentSq + insideWeight * insideWeight * widthSquared;
1362
+ const insideMiterOk = pDist <= smallerInsideDiagonalSq;
1363
+ if (insideMiterOk) {
1364
+ if (style.join === "bevel" || pDist / widthSquared > miterLimitSquared) {
1365
+ if (clockwise) {
1366
+ verts.push(imx, imy);
1367
+ verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
1368
+ verts.push(imx, imy);
1369
+ verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
1370
+ } else {
1371
+ verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
1372
+ verts.push(omx, omy);
1373
+ verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
1374
+ verts.push(omx, omy);
1375
+ }
1376
+ indexCount += 2;
1377
+ } else if (style.join === "round") {
1378
+ if (clockwise) {
1379
+ verts.push(imx, imy);
1380
+ verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
1381
+ indexCount += round(
1382
+ x1,
1383
+ y1,
1384
+ x1 + perpX * outerWeight,
1385
+ y1 + perpY * outerWeight,
1386
+ x1 + perp1x * outerWeight,
1387
+ y1 + perp1y * outerWeight,
1388
+ verts,
1389
+ true
1390
+ ) + 4;
1391
+ verts.push(imx, imy);
1392
+ verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
1393
+ } else {
1394
+ verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
1395
+ verts.push(omx, omy);
1396
+ indexCount += round(
1397
+ x1,
1398
+ y1,
1399
+ x1 - perpX * innerWeight,
1400
+ y1 - perpY * innerWeight,
1401
+ x1 - perp1x * innerWeight,
1402
+ y1 - perp1y * innerWeight,
1403
+ verts,
1404
+ false
1405
+ ) + 4;
1406
+ verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
1407
+ verts.push(omx, omy);
1408
+ }
1409
+ } else {
1410
+ verts.push(imx, imy);
1411
+ verts.push(omx, omy);
1412
+ }
1413
+ } else {
1414
+ verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
1415
+ verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
1416
+ if (style.join === "round") {
1417
+ if (clockwise) {
1418
+ indexCount += round(
1419
+ x1,
1420
+ y1,
1421
+ x1 + perpX * outerWeight,
1422
+ y1 + perpY * outerWeight,
1423
+ x1 + perp1x * outerWeight,
1424
+ y1 + perp1y * outerWeight,
1425
+ verts,
1426
+ true
1427
+ ) + 2;
1428
+ } else {
1429
+ indexCount += round(
1430
+ x1,
1431
+ y1,
1432
+ x1 - perpX * innerWeight,
1433
+ y1 - perpY * innerWeight,
1434
+ x1 - perp1x * innerWeight,
1435
+ y1 - perp1y * innerWeight,
1436
+ verts,
1437
+ false
1438
+ ) + 2;
1439
+ }
1440
+ } else if (style.join === "miter" && pDist / widthSquared <= miterLimitSquared) {
1441
+ if (clockwise) {
1442
+ verts.push(omx, omy);
1443
+ verts.push(omx, omy);
1444
+ } else {
1445
+ verts.push(imx, imy);
1446
+ verts.push(imx, imy);
1447
+ }
1448
+ indexCount += 2;
1449
+ }
1450
+ verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
1451
+ verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
1452
+ indexCount += 2;
1453
+ }
1454
+ }
1455
+ x0 = points[(length - 2) * 2];
1456
+ y0 = points[(length - 2) * 2 + 1];
1457
+ x1 = points[(length - 1) * 2];
1458
+ y1 = points[(length - 1) * 2 + 1];
1459
+ perpX = -(y0 - y1);
1460
+ perpY = x0 - x1;
1461
+ dist = Math.sqrt(perpX * perpX + perpY * perpY);
1462
+ perpX /= dist;
1463
+ perpY /= dist;
1464
+ perpX *= width;
1465
+ perpY *= width;
1466
+ verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
1467
+ verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
1468
+ if (!closedShape) {
1469
+ if (style.cap === "round") {
1470
+ indexCount += round(
1471
+ x1 - perpX * (innerWeight - outerWeight) * 0.5,
1472
+ y1 - perpY * (innerWeight - outerWeight) * 0.5,
1473
+ x1 - perpX * innerWeight,
1474
+ y1 - perpY * innerWeight,
1475
+ x1 + perpX * outerWeight,
1476
+ y1 + perpY * outerWeight,
1477
+ verts,
1478
+ false
1479
+ ) + 2;
1480
+ } else if (style.cap === "square") {
1481
+ indexCount += square(x1, y1, perpX, perpY, innerWeight, outerWeight, false, verts);
1482
+ }
1483
+ }
1484
+ const eps2 = curveEps * curveEps;
1485
+ for (let i = indexStart; i < indexCount + indexStart - 2; ++i) {
1486
+ x0 = verts[i * 2];
1487
+ y0 = verts[i * 2 + 1];
1488
+ x1 = verts[(i + 1) * 2];
1489
+ y1 = verts[(i + 1) * 2 + 1];
1490
+ x2 = verts[(i + 2) * 2];
1491
+ y2 = verts[(i + 2) * 2 + 1];
1492
+ if (Math.abs(x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)) < eps2) {
1493
+ continue;
1494
+ }
1495
+ indices.push(i, i + 1, i + 2);
1496
+ }
1497
+ return {
1498
+ vertices,
1499
+ indices
1500
+ };
1501
+ }
1502
+ function getOrientationOfPoints(points) {
1503
+ const m = points.length;
1504
+ if (m < 6) {
1505
+ return 1;
1506
+ }
1507
+ let area = 0;
1508
+ for (let i = 0, x1 = points[m - 2], y1 = points[m - 1]; i < m; i += 2) {
1509
+ const x2 = points[i];
1510
+ const y2 = points[i + 1];
1511
+ area += (x2 - x1) * (y2 + y1);
1512
+ x1 = x2;
1513
+ y1 = y2;
1514
+ }
1515
+ if (area < 0) {
1516
+ return -1;
1517
+ }
1518
+ return 1;
1519
+ }
1520
+ function square(x, y, nx, ny, innerWeight, outerWeight, clockwise, verts) {
1521
+ const ix = x - nx * innerWeight;
1522
+ const iy = y - ny * innerWeight;
1523
+ const ox = x + nx * outerWeight;
1524
+ const oy = y + ny * outerWeight;
1525
+ let exx;
1526
+ let eyy;
1527
+ if (clockwise) {
1528
+ exx = ny;
1529
+ eyy = -nx;
1530
+ } else {
1531
+ exx = -ny;
1532
+ eyy = nx;
1533
+ }
1534
+ const eix = ix + exx;
1535
+ const eiy = iy + eyy;
1536
+ const eox = ox + exx;
1537
+ const eoy = oy + eyy;
1538
+ verts.push(eix, eiy);
1539
+ verts.push(eox, eoy);
1540
+ return 2;
1541
+ }
1542
+ function round(cx, cy, sx, sy, ex, ey, verts, clockwise) {
1543
+ const cx2p0x = sx - cx;
1544
+ const cy2p0y = sy - cy;
1545
+ let angle0 = Math.atan2(cx2p0x, cy2p0y);
1546
+ let angle1 = Math.atan2(ex - cx, ey - cy);
1547
+ if (clockwise && angle0 < angle1) {
1548
+ angle0 += Math.PI * 2;
1549
+ } else if (!clockwise && angle0 > angle1) {
1550
+ angle1 += Math.PI * 2;
1551
+ }
1552
+ let startAngle = angle0;
1553
+ const angleDiff = angle1 - angle0;
1554
+ const absAngleDiff = Math.abs(angleDiff);
1555
+ const radius = Math.sqrt(cx2p0x * cx2p0x + cy2p0y * cy2p0y);
1556
+ const segCount = (15 * absAngleDiff * Math.sqrt(radius) / Math.PI >> 0) + 1;
1557
+ const angleInc = angleDiff / segCount;
1558
+ startAngle += angleInc;
1559
+ if (clockwise) {
1560
+ verts.push(cx, cy);
1561
+ verts.push(sx, sy);
1562
+ for (let i = 1, angle = startAngle; i < segCount; i++, angle += angleInc) {
1563
+ verts.push(cx, cy);
1564
+ verts.push(cx + Math.sin(angle) * radius, cy + Math.cos(angle) * radius);
1565
+ }
1566
+ verts.push(cx, cy);
1567
+ verts.push(ex, ey);
1568
+ } else {
1569
+ verts.push(sx, sy);
1570
+ verts.push(cx, cy);
1571
+ for (let i = 1, angle = startAngle; i < segCount; i++, angle += angleInc) {
1572
+ verts.push(cx + Math.sin(angle) * radius, cy + Math.cos(angle) * radius);
1573
+ verts.push(cx, cy);
1574
+ }
1575
+ verts.push(ex, ey);
1576
+ verts.push(cx, cy);
1577
+ }
1578
+ return segCount * 2;
1579
+ }
1580
+
995
1581
  class Curve {
996
- arcLengthDivisions = 200;
997
- _cacheArcLengths;
998
- _needsUpdate = false;
1582
+ startT = 0;
1583
+ endT = 1;
1584
+ arcLengthDivision = 200;
1585
+ _arcLengths;
1586
+ getPointAt(u, output = new Vector2()) {
1587
+ return this.getPoint(this.getUToTMapping(u), output);
1588
+ }
999
1589
  isClockwise() {
1000
1590
  const prev = this.getPoint(1);
1001
1591
  const cur = this.getPoint(0.5);
1002
1592
  const next = this.getPoint(1);
1003
1593
  return (cur.x - prev.x) * (next.y - cur.y) - (cur.y - prev.y) * (next.x - cur.x) < 0;
1004
1594
  }
1005
- getPointAt(u, output = new Vector2()) {
1006
- return this.getPoint(this.getUToTMapping(u), output);
1595
+ getControlPointRefs() {
1596
+ return [];
1007
1597
  }
1008
- getPoints(divisions = 5) {
1009
- const points = [];
1010
- for (let i = 0; i <= divisions; i++) {
1011
- points.push(this.getPoint(i / divisions));
1598
+ applyTransform(transform) {
1599
+ this.getControlPointRefs().forEach((p) => {
1600
+ p.applyMatrix3(transform);
1601
+ });
1602
+ return this;
1603
+ }
1604
+ getUnevenPointArray(count = 5, output = []) {
1605
+ const p = new Vector2();
1606
+ for (let i = 0, len = Math.max(1, count) - 1; i <= len; i++) {
1607
+ this.getPoint(i / len, p);
1608
+ output.push(p.x, p.y);
1012
1609
  }
1013
- return points;
1610
+ return output;
1014
1611
  }
1015
- forEachControlPoints(cb) {
1016
- this.getControlPoints().forEach(cb);
1017
- return this;
1612
+ getSpacedPointArray(count = 5, output = []) {
1613
+ const p = new Vector2();
1614
+ for (let i = 0, len = Math.max(1, count) - 1; i <= len; i++) {
1615
+ this.getPointAt(i / len, p);
1616
+ output.push(p.x, p.y);
1617
+ }
1618
+ return output;
1018
1619
  }
1019
- getSpacedPoints(divisions = 5) {
1020
- const points = [];
1021
- for (let i = 0; i <= divisions; i++) {
1022
- points.push(this.getPointAt(i / divisions));
1620
+ getAdaptivePointArray(output = []) {
1621
+ return this.getUnevenPointArray(5, output);
1622
+ }
1623
+ _pointArrayToPoint(array, output = []) {
1624
+ for (let i = 0, len = array.length; i < len; i += 2) {
1625
+ const x = array[i];
1626
+ const y = array[i + 1];
1627
+ output.push(new Vector2(x, y));
1023
1628
  }
1024
- return points;
1629
+ return output;
1630
+ }
1631
+ getSpacedPoints(count, output = []) {
1632
+ const array = this.getSpacedPointArray(count);
1633
+ this._pointArrayToPoint(array, output);
1634
+ return output;
1635
+ }
1636
+ getUnevenPoints(count, output = []) {
1637
+ const array = this.getUnevenPointArray(count);
1638
+ this._pointArrayToPoint(array, output);
1639
+ return output;
1640
+ }
1641
+ getAdaptivePoints(output = []) {
1642
+ const array = this.getAdaptivePointArray();
1643
+ this._pointArrayToPoint(array, output);
1644
+ return output;
1645
+ }
1646
+ getPoints(count, output = []) {
1647
+ let array;
1648
+ if (count) {
1649
+ array = this.getUnevenPointArray(count);
1650
+ } else {
1651
+ array = this.getAdaptivePointArray();
1652
+ }
1653
+ this._pointArrayToPoint(array, output);
1654
+ return output;
1025
1655
  }
1026
1656
  getLength() {
1027
1657
  const lengths = this.getLengths();
1028
1658
  return lengths[lengths.length - 1];
1029
1659
  }
1030
- getLengths(divisions = this.arcLengthDivisions) {
1031
- if (this._cacheArcLengths && this._cacheArcLengths.length === divisions + 1 && !this._needsUpdate) {
1032
- return this._cacheArcLengths;
1033
- }
1034
- this._needsUpdate = false;
1035
- const cache = [];
1036
- let current;
1037
- let last = this.getPoint(0);
1038
- let sum = 0;
1039
- cache.push(0);
1040
- for (let i = 1; i <= divisions; i++) {
1041
- current = this.getPoint(i / divisions);
1042
- sum += current.distanceTo(last);
1043
- cache.push(sum);
1044
- last = current;
1045
- }
1046
- this._cacheArcLengths = cache;
1047
- return cache;
1048
- }
1049
- updateArcLengths() {
1050
- this._needsUpdate = true;
1051
- this.getLengths();
1660
+ getLengths() {
1661
+ if (!this._arcLengths || this._arcLengths.length !== this.arcLengthDivision + 1) {
1662
+ this.updateLengths();
1663
+ }
1664
+ return this._arcLengths;
1665
+ }
1666
+ updateLengths() {
1667
+ const divisions = this.arcLengthDivision;
1668
+ const arcLengths = [0];
1669
+ for (let sum = 0, prev = this.getPoint(0), i = 1; i <= divisions; i++) {
1670
+ const current = this.getPoint(i / divisions);
1671
+ sum += current.distanceTo(prev);
1672
+ arcLengths.push(sum);
1673
+ prev = current;
1674
+ }
1675
+ this._arcLengths = arcLengths;
1052
1676
  }
1053
1677
  getUToTMapping(u, distance) {
1054
1678
  const lengths = this.getLengths();
1055
- let i = 0;
1056
1679
  const lengthsLen = lengths.length;
1057
- let targetLength;
1058
- if (distance) {
1059
- targetLength = distance;
1060
- } else {
1061
- targetLength = u * lengths[lengthsLen - 1];
1680
+ const targetLen = distance ?? u * lengths[lengthsLen - 1];
1681
+ if (lengthsLen < 2) {
1682
+ return targetLen / lengths[0];
1062
1683
  }
1684
+ let i = 0;
1063
1685
  let low = 0;
1064
1686
  let high = lengthsLen - 1;
1065
1687
  let comparison;
1066
1688
  while (low <= high) {
1067
1689
  i = Math.floor(low + (high - low) / 2);
1068
- comparison = lengths[i] - targetLength;
1690
+ comparison = lengths[i] - targetLen;
1069
1691
  if (comparison < 0) {
1070
1692
  low = i + 1;
1071
1693
  } else if (comparison > 0) {
@@ -1076,13 +1698,13 @@ class Curve {
1076
1698
  }
1077
1699
  }
1078
1700
  i = high;
1079
- if (lengths[i] === targetLength) {
1701
+ if (lengths[i] === targetLen) {
1080
1702
  return i / (lengthsLen - 1);
1081
1703
  }
1082
- const lengthBefore = lengths[i];
1083
- const lengthAfter = lengths[i + 1];
1084
- const segmentLength = lengthAfter - lengthBefore;
1085
- const segmentFraction = (targetLength - lengthBefore) / segmentLength;
1704
+ const before = lengths[i];
1705
+ const after = lengths[i + 1];
1706
+ const segmentLength = after - before;
1707
+ const segmentFraction = (targetLen - before) / segmentLength;
1086
1708
  return (i + segmentFraction) / (lengthsLen - 1);
1087
1709
  }
1088
1710
  getTangent(t, output = new Vector2()) {
@@ -1120,32 +1742,52 @@ class Curve {
1120
1742
  }
1121
1743
  return mid;
1122
1744
  }
1123
- matrix(matrix) {
1124
- this.forEachControlPoints((point) => point.applyMatrix3(matrix));
1125
- return this;
1126
- }
1127
1745
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1128
- this.getPoints().forEach((point) => {
1129
- min.min(point);
1130
- max.max(point);
1131
- });
1746
+ const potins = this.getPoints();
1747
+ for (let i = 0, len = potins.length; i < len; i++) {
1748
+ const p = potins[i];
1749
+ min.min(p);
1750
+ max.max(p);
1751
+ }
1132
1752
  return { min, max };
1133
1753
  }
1134
1754
  getBoundingBox() {
1135
1755
  const { min, max } = this.getMinMax();
1136
1756
  return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
1137
1757
  }
1758
+ fillTriangulate(options) {
1759
+ return fillTriangulate(
1760
+ this.getPoints().reduce((arr, p) => {
1761
+ arr.push(p.x, p.y);
1762
+ return arr;
1763
+ }, []),
1764
+ options
1765
+ );
1766
+ }
1767
+ strokeTriangulate(options) {
1768
+ return strokeTriangulate(
1769
+ this.getPoints().reduce((arr, p) => {
1770
+ arr.push(p.x, p.y);
1771
+ return arr;
1772
+ }, []),
1773
+ options
1774
+ );
1775
+ }
1138
1776
  toCommands() {
1139
- return this.getPoints().map((point, i) => {
1777
+ const comds = [];
1778
+ const potins = this.getPoints();
1779
+ for (let i = 0, len = potins.length; i < len; i++) {
1780
+ const p = potins[i];
1140
1781
  if (i === 0) {
1141
- return { type: "M", x: point.x, y: point.y };
1782
+ comds.push({ type: "M", x: p.x, y: p.y });
1142
1783
  } else {
1143
- return { type: "L", x: point.x, y: point.y };
1784
+ comds.push({ type: "L", x: p.x, y: p.y });
1144
1785
  }
1145
- });
1786
+ }
1787
+ return comds;
1146
1788
  }
1147
1789
  toData() {
1148
- return pathCommandsToPathData(this.toCommands());
1790
+ return svgPathCommandsToData(this.toCommands());
1149
1791
  }
1150
1792
  drawTo(ctx) {
1151
1793
  this.toCommands().forEach((cmd) => {
@@ -1161,7 +1803,7 @@ class Curve {
1161
1803
  return this;
1162
1804
  }
1163
1805
  copy(source) {
1164
- this.arcLengthDivisions = source.arcLengthDivisions;
1806
+ this.arcLengthDivision = source.arcLengthDivision;
1165
1807
  return this;
1166
1808
  }
1167
1809
  clone() {
@@ -1169,178 +1811,56 @@ class Curve {
1169
1811
  }
1170
1812
  }
1171
1813
 
1172
- class CircleCurve extends Curve {
1173
- constructor(center, radius, start = 0, end = Math.PI * 2) {
1814
+ const tempTransform0$1 = new Matrix3();
1815
+ const tempTransform1$1 = new Matrix3();
1816
+ const tempTransform2$1 = new Matrix3();
1817
+ const tempV2 = new Vector2();
1818
+ class RoundCurve extends Curve {
1819
+ constructor(_center, _radius, _diff, rotate = 0, startAngle = 0, endAngle = Math.PI * 2, clockwise = false) {
1174
1820
  super();
1175
- this.center = center;
1176
- this.radius = radius;
1177
- this.start = start;
1178
- this.end = end;
1179
- }
1180
- getPoint(t) {
1181
- const { radius, center } = this;
1182
- return center.clone().add(this.getNormal(t).clone().scale(radius));
1821
+ this._center = _center;
1822
+ this._radius = _radius;
1823
+ this._diff = _diff;
1824
+ this.rotate = rotate;
1825
+ this.startAngle = startAngle;
1826
+ this.endAngle = endAngle;
1827
+ this.clockwise = clockwise;
1183
1828
  }
1184
- getTangent(t, output = new Vector2()) {
1185
- const { x, y } = this.getNormal(t);
1186
- return output.set(-y, x);
1829
+ get cx() {
1830
+ return this._center.x;
1187
1831
  }
1188
- getNormal(t, output = new Vector2()) {
1189
- const { start, end } = this;
1190
- const _t = t * (end - start) + start - 0.5 * Math.PI;
1191
- return output.set(Math.cos(_t), Math.sin(_t));
1832
+ set cx(val) {
1833
+ this._center.x = val;
1192
1834
  }
1193
- getControlPoints() {
1194
- return [this.center];
1835
+ get cy() {
1836
+ return this._center.y;
1195
1837
  }
1196
- getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1197
- min.x = Math.min(min.x, this.center.x - this.radius);
1198
- min.y = Math.min(min.y, this.center.y - this.radius);
1199
- max.x = Math.max(max.x, this.center.x + this.radius);
1200
- max.y = Math.max(max.y, this.center.y + this.radius);
1201
- return { min, max };
1202
- }
1203
- }
1204
-
1205
- function catmullRom(t, p0, p1, p2, p3) {
1206
- const v0 = (p2 - p0) * 0.5;
1207
- const v1 = (p3 - p1) * 0.5;
1208
- const t2 = t * t;
1209
- const t3 = t * t2;
1210
- return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1;
1211
- }
1212
- function quadraticBezierP0(t, p) {
1213
- const k = 1 - t;
1214
- return k * k * p;
1215
- }
1216
- function quadraticBezierP1(t, p) {
1217
- return 2 * (1 - t) * t * p;
1218
- }
1219
- function quadraticBezierP2(t, p) {
1220
- return t * t * p;
1221
- }
1222
- function quadraticBezier(t, p0, p1, p2) {
1223
- return quadraticBezierP0(t, p0) + quadraticBezierP1(t, p1) + quadraticBezierP2(t, p2);
1224
- }
1225
- function cubicBezierP0(t, p) {
1226
- const k = 1 - t;
1227
- return k * k * k * p;
1228
- }
1229
- function cubicBezierP1(t, p) {
1230
- const k = 1 - t;
1231
- return 3 * k * k * t * p;
1232
- }
1233
- function cubicBezierP2(t, p) {
1234
- return 3 * (1 - t) * t * t * p;
1235
- }
1236
- function cubicBezierP3(t, p) {
1237
- return t * t * t * p;
1238
- }
1239
- function cubicBezier(t, p0, p1, p2, p3) {
1240
- return cubicBezierP0(t, p0) + cubicBezierP1(t, p1) + cubicBezierP2(t, p2) + cubicBezierP3(t, p3);
1241
- }
1242
-
1243
- class CubicBezierCurve extends Curve {
1244
- constructor(start = new Vector2(), startControl = new Vector2(), endControl = new Vector2(), end = new Vector2()) {
1245
- super();
1246
- this.start = start;
1247
- this.startControl = startControl;
1248
- this.endControl = endControl;
1249
- this.end = end;
1838
+ set cy(val) {
1839
+ this._center.y = val;
1250
1840
  }
1251
- getPoint(t, output = new Vector2()) {
1252
- const { start, startControl, endControl, end } = this;
1253
- return output.set(
1254
- cubicBezier(t, start.x, startControl.x, endControl.x, end.x),
1255
- cubicBezier(t, start.y, startControl.y, endControl.y, end.y)
1256
- );
1841
+ get rx() {
1842
+ return this._radius.x;
1257
1843
  }
1258
- getControlPoints() {
1259
- return [
1260
- this.start,
1261
- this.startControl,
1262
- this.endControl,
1263
- this.end
1264
- ];
1844
+ set rx(val) {
1845
+ this._radius.x = val;
1265
1846
  }
1266
- _solveQuadratic(a, b, c) {
1267
- const discriminant = b * b - 4 * a * c;
1268
- if (discriminant < 0)
1269
- return [];
1270
- const sqrtDiscriminant = Math.sqrt(discriminant);
1271
- const t1 = (-b + sqrtDiscriminant) / (2 * a);
1272
- const t2 = (-b - sqrtDiscriminant) / (2 * a);
1273
- return [t1, t2].filter((t) => t >= 0 && t <= 1);
1847
+ get ry() {
1848
+ return this._radius.y;
1274
1849
  }
1275
- getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1276
- const p0 = this.start;
1277
- const p1 = this.startControl;
1278
- const p2 = this.endControl;
1279
- const p3 = this.end;
1280
- const dxRoots = this._solveQuadratic(
1281
- 3 * (p1.x - p0.x),
1282
- 6 * (p2.x - p1.x),
1283
- 3 * (p3.x - p2.x)
1284
- );
1285
- const dyRoots = this._solveQuadratic(
1286
- 3 * (p1.y - p0.y),
1287
- 6 * (p2.y - p1.y),
1288
- 3 * (p3.y - p2.y)
1289
- );
1290
- const tValues = [0, 1, ...dxRoots, ...dyRoots];
1291
- const samplePoints = (tValues2, precision) => {
1292
- for (const t of tValues2) {
1293
- for (let i = 0; i <= precision; i++) {
1294
- const delta = i / precision - 0.5;
1295
- const refinedT = Math.min(1, Math.max(0, t + delta));
1296
- const point = this.getPoint(refinedT);
1297
- min.x = Math.min(min.x, point.x);
1298
- min.y = Math.min(min.y, point.y);
1299
- max.x = Math.max(max.x, point.x);
1300
- max.y = Math.max(max.y, point.y);
1301
- }
1302
- }
1303
- };
1304
- samplePoints(tValues, 10);
1305
- return { min, max };
1850
+ set ry(val) {
1851
+ this._radius.y = val;
1306
1852
  }
1307
- toCommands() {
1308
- const { start, startControl, endControl, end } = this;
1309
- return [
1310
- { type: "M", x: start.x, y: start.y },
1311
- { type: "C", x1: startControl.x, y1: startControl.y, x2: endControl.x, y2: endControl.y, x: end.x, y: end.y }
1312
- ];
1853
+ get dx() {
1854
+ return this._diff.x;
1313
1855
  }
1314
- drawTo(ctx) {
1315
- const { start, startControl, endControl, end } = this;
1316
- ctx.lineTo(start.x, start.y);
1317
- ctx.bezierCurveTo(startControl.x, startControl.y, endControl.x, endControl.y, end.x, end.y);
1318
- return this;
1856
+ set dx(val) {
1857
+ this._diff.x = val;
1319
1858
  }
1320
- copy(source) {
1321
- super.copy(source);
1322
- this.start.copy(source.start);
1323
- this.startControl.copy(source.startControl);
1324
- this.endControl.copy(source.endControl);
1325
- this.end.copy(source.end);
1326
- return this;
1859
+ get dy() {
1860
+ return this._diff.y;
1327
1861
  }
1328
- }
1329
-
1330
- const tempTransform0$1 = new Matrix3();
1331
- const tempTransform1$1 = new Matrix3();
1332
- const tempTransform2$1 = new Matrix3();
1333
- const tempV2 = new Vector2();
1334
- class EllipseCurve extends Curve {
1335
- constructor(center = new Vector2(), radiusX = 1, radiusY = 1, rotation = 0, startAngle = 0, endAngle = Math.PI * 2, clockwise = false) {
1336
- super();
1337
- this.center = center;
1338
- this.radiusX = radiusX;
1339
- this.radiusY = radiusY;
1340
- this.rotation = rotation;
1341
- this.startAngle = startAngle;
1342
- this.endAngle = endAngle;
1343
- this.clockwise = clockwise;
1862
+ set dy(val) {
1863
+ this._diff.y = val;
1344
1864
  }
1345
1865
  isClockwise() {
1346
1866
  return this.clockwise;
@@ -1366,39 +1886,38 @@ class EllipseCurve extends Curve {
1366
1886
  }
1367
1887
  }
1368
1888
  const angle = this.startAngle + t * deltaAngle;
1369
- let _x = this.center.x + this.radiusX * Math.cos(angle);
1370
- let _y = this.center.y + this.radiusY * Math.sin(angle);
1371
- if (this.rotation !== 0) {
1372
- const cos = Math.cos(this.rotation);
1373
- const sin = Math.sin(this.rotation);
1374
- const tx = _x - this.center.x;
1375
- const ty = _y - this.center.y;
1376
- _x = tx * cos - ty * sin + this.center.x;
1377
- _y = tx * sin + ty * cos + this.center.y;
1889
+ let _x = this.cx + this.rx * Math.cos(angle);
1890
+ let _y = this.cy + this.ry * Math.sin(angle);
1891
+ if (this.rotate !== 0) {
1892
+ const cos = Math.cos(this.rotate);
1893
+ const sin = Math.sin(this.rotate);
1894
+ const tx = _x - this.cx;
1895
+ const ty = _y - this.cy;
1896
+ _x = tx * cos - ty * sin + this.cx;
1897
+ _y = tx * sin + ty * cos + this.cy;
1378
1898
  }
1379
1899
  return output.set(_x, _y);
1380
1900
  }
1381
1901
  toCommands() {
1382
- const { center, radiusX: rx, radiusY: ry, startAngle, endAngle, clockwise, rotation } = this;
1383
- const { x: cx, y: cy } = center;
1384
- const startX = cx + rx * Math.cos(startAngle) * Math.cos(rotation) - ry * Math.sin(startAngle) * Math.sin(rotation);
1385
- const startY = cy + rx * Math.cos(startAngle) * Math.sin(rotation) + ry * Math.sin(startAngle) * Math.cos(rotation);
1902
+ const { cx, cy, rx, ry, startAngle, endAngle, clockwise, rotate } = this;
1903
+ const startX = cx + rx * Math.cos(startAngle) * Math.cos(rotate) - ry * Math.sin(startAngle) * Math.sin(rotate);
1904
+ const startY = cy + rx * Math.cos(startAngle) * Math.sin(rotate) + ry * Math.sin(startAngle) * Math.cos(rotate);
1386
1905
  const angleDiff = Math.abs(startAngle - endAngle);
1387
1906
  const largeArcFlag = angleDiff > Math.PI ? 1 : 0;
1388
1907
  const sweepFlag = clockwise ? 1 : 0;
1389
- const angle = rotation * 180 / Math.PI;
1908
+ const angle = rotate * 180 / Math.PI;
1390
1909
  if (angleDiff >= 2 * Math.PI) {
1391
1910
  const midAngle = startAngle + Math.PI;
1392
- const midX = cx + rx * Math.cos(midAngle) * Math.cos(rotation) - ry * Math.sin(midAngle) * Math.sin(rotation);
1393
- const midY = cy + rx * Math.cos(midAngle) * Math.sin(rotation) + ry * Math.sin(midAngle) * Math.cos(rotation);
1911
+ const midX = cx + rx * Math.cos(midAngle) * Math.cos(rotate) - ry * Math.sin(midAngle) * Math.sin(rotate);
1912
+ const midY = cy + rx * Math.cos(midAngle) * Math.sin(rotate) + ry * Math.sin(midAngle) * Math.cos(rotate);
1394
1913
  return [
1395
1914
  { type: "M", x: startX, y: startY },
1396
1915
  { type: "A", rx, ry, angle, largeArcFlag: 0, sweepFlag, x: midX, y: midY },
1397
1916
  { type: "A", rx, ry, angle, largeArcFlag: 0, sweepFlag, x: startX, y: startY }
1398
1917
  ];
1399
1918
  } else {
1400
- const endX = cx + rx * Math.cos(endAngle) * Math.cos(rotation) - ry * Math.sin(endAngle) * Math.sin(rotation);
1401
- const endY = cy + rx * Math.cos(endAngle) * Math.sin(rotation) + ry * Math.sin(endAngle) * Math.cos(rotation);
1919
+ const endX = cx + rx * Math.cos(endAngle) * Math.cos(rotate) - ry * Math.sin(endAngle) * Math.sin(rotate);
1920
+ const endY = cy + rx * Math.cos(endAngle) * Math.sin(rotate) + ry * Math.sin(endAngle) * Math.cos(rotate);
1402
1921
  return [
1403
1922
  { type: "M", x: startX, y: startY },
1404
1923
  { type: "A", rx, ry, angle, largeArcFlag, sweepFlag, x: endX, y: endY }
@@ -1406,24 +1925,24 @@ class EllipseCurve extends Curve {
1406
1925
  }
1407
1926
  }
1408
1927
  drawTo(ctx) {
1409
- const { center, radiusX, radiusY, rotation, startAngle, endAngle, clockwise } = this;
1928
+ const { cx, cy, rx, ry, rotate, startAngle, endAngle, clockwise } = this;
1410
1929
  ctx.ellipse(
1411
- center.x,
1412
- center.y,
1413
- radiusX,
1414
- radiusY,
1415
- rotation,
1930
+ cx,
1931
+ cy,
1932
+ rx,
1933
+ ry,
1934
+ rotate,
1416
1935
  startAngle,
1417
1936
  endAngle,
1418
1937
  !clockwise
1419
1938
  );
1420
1939
  return this;
1421
1940
  }
1422
- matrix(matrix) {
1423
- tempV2.set(this.center.x, this.center.y);
1941
+ applyTransform(matrix) {
1942
+ tempV2.set(this.cx, this.cy);
1424
1943
  tempV2.applyMatrix3(matrix);
1425
- this.center.x = tempV2.x;
1426
- this.center.y = tempV2.y;
1944
+ this.cx = tempV2.x;
1945
+ this.cy = tempV2.y;
1427
1946
  if (isTransformSkewed(matrix)) {
1428
1947
  transfEllipseGeneric(this, matrix);
1429
1948
  } else {
@@ -1431,14 +1950,129 @@ class EllipseCurve extends Curve {
1431
1950
  }
1432
1951
  return this;
1433
1952
  }
1434
- getControlPoints() {
1435
- return [this.center];
1953
+ getControlPointRefs() {
1954
+ return [this._center];
1955
+ }
1956
+ getAdaptivePointArray(output = []) {
1957
+ const { cx, cy, rx, ry, dx, dy } = this;
1958
+ if (!(rx >= 0 && ry >= 0 && dx >= 0 && dy >= 0)) {
1959
+ return output;
1960
+ }
1961
+ const n = Math.ceil(2.3 * Math.sqrt(rx + ry));
1962
+ const m = n * 8 + (dx ? 4 : 0) + (dy ? 4 : 0);
1963
+ if (m === 0) {
1964
+ return output;
1965
+ }
1966
+ const array = [];
1967
+ if (n === 0) {
1968
+ array[0] = array[6] = cx + dx;
1969
+ array[1] = array[3] = cy + dy;
1970
+ array[2] = array[4] = cx - dx;
1971
+ array[5] = array[7] = cy - dy;
1972
+ } else {
1973
+ let j1 = 0;
1974
+ let j2 = n * 4 + (dx ? 2 : 0) + 2;
1975
+ let j3 = j2;
1976
+ let j4 = m;
1977
+ let x0 = dx + rx;
1978
+ let y0 = dy;
1979
+ let x1 = cx + x0;
1980
+ let x2 = cx - x0;
1981
+ let y1 = cy + y0;
1982
+ array[j1++] = x1;
1983
+ array[j1++] = y1;
1984
+ array[--j2] = y1;
1985
+ array[--j2] = x2;
1986
+ if (dy) {
1987
+ const y22 = cy - y0;
1988
+ array[j3++] = x2;
1989
+ array[j3++] = y22;
1990
+ array[--j4] = y22;
1991
+ array[--j4] = x1;
1992
+ }
1993
+ for (let i = 1; i < n; i++) {
1994
+ const a = Math.PI / 2 * (i / n);
1995
+ const x02 = dx + Math.cos(a) * rx;
1996
+ const y02 = dy + Math.sin(a) * ry;
1997
+ const x12 = cx + x02;
1998
+ const x22 = cx - x02;
1999
+ const y12 = cy + y02;
2000
+ const y22 = cy - y02;
2001
+ array[j1++] = x12;
2002
+ array[j1++] = y12;
2003
+ array[--j2] = y12;
2004
+ array[--j2] = x22;
2005
+ array[j3++] = x22;
2006
+ array[j3++] = y22;
2007
+ array[--j4] = y22;
2008
+ array[--j4] = x12;
2009
+ }
2010
+ x0 = dx;
2011
+ y0 = dy + ry;
2012
+ x1 = cx + x0;
2013
+ x2 = cx - x0;
2014
+ y1 = cy + y0;
2015
+ const y2 = cy - y0;
2016
+ array[j1++] = x1;
2017
+ array[j1++] = y1;
2018
+ array[--j4] = y2;
2019
+ array[--j4] = x1;
2020
+ if (dx) {
2021
+ array[j1++] = x2;
2022
+ array[j1++] = y1;
2023
+ array[--j4] = y2;
2024
+ array[--j4] = x2;
2025
+ }
2026
+ }
2027
+ Array.prototype.push.apply(output, array);
2028
+ return output;
2029
+ }
2030
+ fillTriangulate(options = {}) {
2031
+ let {
2032
+ vertices = [],
2033
+ indices = [],
2034
+ verticesStride = 2,
2035
+ verticesOffset = 0,
2036
+ indicesOffset = 0
2037
+ } = options;
2038
+ const points = this.getAdaptivePointArray();
2039
+ if (points.length === 0) {
2040
+ return { vertices, indices };
2041
+ }
2042
+ let centerX = 0;
2043
+ let centerY = 0;
2044
+ for (let i = 0; i < points.length; i += 2) {
2045
+ centerX += points[i];
2046
+ centerY += points[i + 1];
2047
+ }
2048
+ centerX /= points.length / 2;
2049
+ centerY /= points.length / 2;
2050
+ let count = verticesOffset;
2051
+ vertices[count * verticesStride] = centerX;
2052
+ vertices[count * verticesStride + 1] = centerY;
2053
+ const centerIndex = count++;
2054
+ for (let i = 0; i < points.length; i += 2) {
2055
+ vertices[count * verticesStride] = points[i];
2056
+ vertices[count * verticesStride + 1] = points[i + 1];
2057
+ if (i > 0) {
2058
+ indices[indicesOffset++] = count;
2059
+ indices[indicesOffset++] = centerIndex;
2060
+ indices[indicesOffset++] = count - 1;
2061
+ }
2062
+ count++;
2063
+ }
2064
+ indices[indicesOffset++] = centerIndex + 1;
2065
+ indices[indicesOffset++] = centerIndex;
2066
+ indices[indicesOffset++] = count - 1;
2067
+ return {
2068
+ vertices,
2069
+ indices
2070
+ };
1436
2071
  }
1437
2072
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1438
- const { center, radiusX: rx, radiusY: ry, rotation: theta } = this;
1439
- const { x: cx, y: cy } = center;
1440
- const cosTheta = Math.cos(theta);
1441
- const sinTheta = Math.sin(theta);
2073
+ const { cx, cy, rx, ry, rotate } = this;
2074
+ const cosTheta = Math.cos(rotate);
2075
+ const sinTheta = Math.sin(rotate);
1442
2076
  const halfWidth = Math.sqrt(
1443
2077
  rx * rx * cosTheta * cosTheta + ry * ry * sinTheta * sinTheta
1444
2078
  );
@@ -1453,22 +2087,22 @@ class EllipseCurve extends Curve {
1453
2087
  }
1454
2088
  copy(source) {
1455
2089
  super.copy(source);
1456
- this.center.x = source.center.x;
1457
- this.center.y = source.center.y;
1458
- this.radiusX = source.radiusX;
1459
- this.radiusY = source.radiusY;
2090
+ this.cx = source.cx;
2091
+ this.cy = source.cy;
2092
+ this.rx = source.rx;
2093
+ this.ry = source.ry;
1460
2094
  this.startAngle = source.startAngle;
1461
2095
  this.endAngle = source.endAngle;
1462
2096
  this.clockwise = source.clockwise;
1463
- this.rotation = source.rotation;
2097
+ this.rotate = source.rotate;
1464
2098
  return this;
1465
2099
  }
1466
2100
  }
1467
2101
  function transfEllipseGeneric(curve, m) {
1468
- const a = curve.radiusX;
1469
- const b = curve.radiusY;
1470
- const cosTheta = Math.cos(curve.rotation);
1471
- const sinTheta = Math.sin(curve.rotation);
2102
+ const a = curve.rx;
2103
+ const b = curve.ry;
2104
+ const cosTheta = Math.cos(curve.rotate);
2105
+ const sinTheta = Math.sin(curve.rotate);
1472
2106
  const v1 = new Vector2(a * cosTheta, a * sinTheta);
1473
2107
  const v2 = new Vector2(-b * sinTheta, b * cosTheta);
1474
2108
  const f1 = v1.applyMatrix3(m);
@@ -1491,9 +2125,9 @@ function transfEllipseGeneric(curve, m) {
1491
2125
  const ed = eigenDecomposition(mQe[0], mQe[1], mQe[4]);
1492
2126
  const rt1sqrt = Math.sqrt(ed.rt1);
1493
2127
  const rt2sqrt = Math.sqrt(ed.rt2);
1494
- curve.radiusX = 1 / rt1sqrt;
1495
- curve.radiusY = 1 / rt2sqrt;
1496
- curve.rotation = Math.atan2(ed.sn, ed.cs);
2128
+ curve.rx = 1 / rt1sqrt;
2129
+ curve.ry = 1 / rt2sqrt;
2130
+ curve.rotate = Math.atan2(ed.sn, ed.cs);
1497
2131
  const isFullEllipse = (curve.endAngle - curve.startAngle) % (2 * Math.PI) < Number.EPSILON;
1498
2132
  if (!isFullEllipse) {
1499
2133
  const mDsqrt = tempTransform1$1.set(
@@ -1533,10 +2167,10 @@ function transfEllipseGeneric(curve, m) {
1533
2167
  function transfEllipseNoSkew(curve, m) {
1534
2168
  const sx = getTransformScaleX(m);
1535
2169
  const sy = getTransformScaleY(m);
1536
- curve.radiusX *= sx;
1537
- curve.radiusY *= sy;
2170
+ curve.rx *= sx;
2171
+ curve.ry *= sy;
1538
2172
  const theta = sx > Number.EPSILON ? Math.atan2(m.elements[1], m.elements[0]) : Math.atan2(-m.elements[3], m.elements[4]);
1539
- curve.rotation += theta;
2173
+ curve.rotate += theta;
1540
2174
  if (isTransformFlipped(m)) {
1541
2175
  curve.startAngle *= -1;
1542
2176
  curve.endAngle *= -1;
@@ -1601,20 +2235,276 @@ function eigenDecomposition(A, B, C) {
1601
2235
  cs = -sn;
1602
2236
  sn = t;
1603
2237
  }
1604
- return { rt1, rt2, cs, sn };
2238
+ return { rt1, rt2, cs, sn };
2239
+ }
2240
+
2241
+ class ArcCurve extends RoundCurve {
2242
+ constructor(cx = 0, cy = 0, radius = 1, startAngle = 0, endAngle = Math.PI * 2, clockwise = false) {
2243
+ super(
2244
+ new Vector2(cx, cy),
2245
+ new Vector2(radius, radius),
2246
+ new Vector2(0, 0),
2247
+ 0,
2248
+ startAngle,
2249
+ endAngle,
2250
+ clockwise
2251
+ );
2252
+ this.radius = radius;
2253
+ }
2254
+ drawTo(ctx) {
2255
+ const { cx, cy, radius, startAngle, endAngle, clockwise } = this;
2256
+ ctx.arc(
2257
+ cx,
2258
+ cy,
2259
+ radius,
2260
+ startAngle,
2261
+ endAngle,
2262
+ !clockwise
2263
+ );
2264
+ return this;
2265
+ }
2266
+ getAdaptivePointArray(output = []) {
2267
+ const { cx, cy, radius, startAngle, endAngle, clockwise } = this;
2268
+ let dist = Math.abs(startAngle - endAngle);
2269
+ if (!clockwise && startAngle > endAngle) {
2270
+ dist = 2 * Math.PI - dist;
2271
+ } else if (clockwise && endAngle > startAngle) {
2272
+ dist = 2 * Math.PI - dist;
2273
+ }
2274
+ let steps = Math.max(6, Math.floor(6 * radius ** (1 / 3) * (dist / Math.PI)));
2275
+ steps = Math.max(steps, 3);
2276
+ let f = dist / steps;
2277
+ let t = startAngle;
2278
+ f *= !clockwise ? -1 : 1;
2279
+ for (let i = 0; i < steps + 1; i++) {
2280
+ const cs = Math.cos(t);
2281
+ const sn = Math.sin(t);
2282
+ const nx = cx + cs * radius;
2283
+ const ny = cy + sn * radius;
2284
+ output.push(nx, ny);
2285
+ t += f;
2286
+ }
2287
+ return output;
2288
+ }
2289
+ }
2290
+
2291
+ class CompositeCurve extends Curve {
2292
+ constructor(curves = []) {
2293
+ super();
2294
+ this.curves = curves;
2295
+ }
2296
+ addCurve(curve) {
2297
+ this.curves.push(curve);
2298
+ return this;
2299
+ }
2300
+ getPoint(t, output = new Vector2()) {
2301
+ const d = t * this.getLength();
2302
+ const lengths = this.getLengths();
2303
+ let i = 0;
2304
+ while (i < lengths.length) {
2305
+ if (lengths[i] >= d) {
2306
+ const diff = lengths[i] - d;
2307
+ const curve = this.curves[i];
2308
+ const length = curve.getLength();
2309
+ return curve.getPointAt(
2310
+ length === 0 ? 0 : 1 - diff / length,
2311
+ output
2312
+ );
2313
+ }
2314
+ i++;
2315
+ }
2316
+ return output;
2317
+ }
2318
+ updateLengths() {
2319
+ const arcLengths = [];
2320
+ for (let i = 0, sum = 0, len = this.curves.length; i < len; i++) {
2321
+ sum += this.curves[i].getLength();
2322
+ arcLengths.push(sum);
2323
+ }
2324
+ this._arcLengths = arcLengths;
2325
+ }
2326
+ getControlPointRefs() {
2327
+ return this.curves.flatMap((curve) => curve.getControlPointRefs());
2328
+ }
2329
+ getAdaptivePointArray(output = []) {
2330
+ let offset;
2331
+ this.curves.forEach((curve) => {
2332
+ curve.getAdaptivePointArray(output);
2333
+ if (offset) {
2334
+ if (output[offset - 1] === output[offset + 1] && output[offset] === output[offset + 2]) {
2335
+ output.splice(offset + 1, 2);
2336
+ }
2337
+ }
2338
+ offset = output.length - 1;
2339
+ });
2340
+ return output;
2341
+ }
2342
+ getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
2343
+ this.curves.forEach((curve) => curve.getMinMax(min, max));
2344
+ return { min, max };
2345
+ }
2346
+ getBoundingBox() {
2347
+ const { min, max } = this.getMinMax();
2348
+ return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
2349
+ }
2350
+ toCommands() {
2351
+ return this.curves.flatMap((curve) => curve.toCommands());
2352
+ }
2353
+ drawTo(ctx) {
2354
+ const point = this.curves[0]?.getPoint(0);
2355
+ if (point) {
2356
+ ctx.moveTo(point.x, point.y);
2357
+ }
2358
+ this.curves.forEach((curve) => curve.drawTo(ctx));
2359
+ return this;
2360
+ }
2361
+ }
2362
+
2363
+ class CubicBezierCurve extends Curve {
2364
+ constructor(p1, cp1, cp2, p2) {
2365
+ super();
2366
+ this.p1 = p1;
2367
+ this.cp1 = cp1;
2368
+ this.cp2 = cp2;
2369
+ this.p2 = p2;
2370
+ }
2371
+ static from(p1x, p1y, cp1x, cp1y, cp2x, cp2y, p2x, p2y) {
2372
+ return new CubicBezierCurve(
2373
+ new Vector2(p1x, p1y),
2374
+ new Vector2(cp1x, cp1y),
2375
+ new Vector2(cp2x, cp2y),
2376
+ new Vector2(p2x, p2y)
2377
+ );
2378
+ }
2379
+ getPoint(t, output = new Vector2()) {
2380
+ const { p1, cp1, cp2, p2 } = this;
2381
+ return output.set(
2382
+ cubicBezier(t, p1.x, cp1.x, cp2.x, p2.x),
2383
+ cubicBezier(t, p1.y, cp1.y, cp2.y, p2.y)
2384
+ );
2385
+ }
2386
+ getAdaptivePointArray(output = []) {
2387
+ return getAdaptiveCubicBezierCurvePoints(
2388
+ this.p1.x,
2389
+ this.p1.y,
2390
+ this.cp1.x,
2391
+ this.cp1.y,
2392
+ this.cp2.x,
2393
+ this.cp2.y,
2394
+ this.p2.x,
2395
+ this.p2.y,
2396
+ 0.5,
2397
+ output
2398
+ );
2399
+ }
2400
+ getControlPointRefs() {
2401
+ return [this.p1, this.cp1, this.cp2, this.p2];
2402
+ }
2403
+ _solveQuadratic(a, b, c) {
2404
+ const discriminant = b * b - 4 * a * c;
2405
+ if (discriminant < 0)
2406
+ return [];
2407
+ const sqrtDiscriminant = Math.sqrt(discriminant);
2408
+ const t1 = (-b + sqrtDiscriminant) / (2 * a);
2409
+ const t2 = (-b - sqrtDiscriminant) / (2 * a);
2410
+ return [t1, t2].filter((t) => t >= 0 && t <= 1);
2411
+ }
2412
+ getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
2413
+ const { p1, cp1, cp2, p2 } = this;
2414
+ const dxRoots = this._solveQuadratic(
2415
+ 3 * (cp1.x - p1.x),
2416
+ 6 * (cp2.x - cp1.x),
2417
+ 3 * (p2.x - cp2.x)
2418
+ );
2419
+ const dyRoots = this._solveQuadratic(
2420
+ 3 * (cp1.y - p1.y),
2421
+ 6 * (cp2.y - cp1.y),
2422
+ 3 * (p2.y - cp2.y)
2423
+ );
2424
+ const tValues = [0, 1, ...dxRoots, ...dyRoots];
2425
+ const samplePoints = (tValues2, precision) => {
2426
+ for (const t of tValues2) {
2427
+ for (let i = 0; i <= precision; i++) {
2428
+ const delta = i / precision - 0.5;
2429
+ const refinedT = Math.min(1, Math.max(0, t + delta));
2430
+ const point = this.getPoint(refinedT);
2431
+ min.x = Math.min(min.x, point.x);
2432
+ min.y = Math.min(min.y, point.y);
2433
+ max.x = Math.max(max.x, point.x);
2434
+ max.y = Math.max(max.y, point.y);
2435
+ }
2436
+ }
2437
+ };
2438
+ samplePoints(tValues, 10);
2439
+ return { min, max };
2440
+ }
2441
+ toCommands() {
2442
+ const { p1, cp1, cp2, p2 } = this;
2443
+ return [
2444
+ { type: "M", x: p1.x, y: p1.y },
2445
+ { type: "C", x1: cp1.x, y1: cp1.y, x2: cp2.x, y2: cp2.y, x: p2.x, y: p2.y }
2446
+ ];
2447
+ }
2448
+ drawTo(ctx) {
2449
+ const { p1, cp1, cp2, p2 } = this;
2450
+ ctx.lineTo(p1.x, p1.y);
2451
+ ctx.bezierCurveTo(cp1.x, cp1.y, cp2.x, cp2.y, p2.x, p2.y);
2452
+ return this;
2453
+ }
2454
+ copy(source) {
2455
+ super.copy(source);
2456
+ this.p1.copy(source.p1);
2457
+ this.cp1.copy(source.cp1);
2458
+ this.cp2.copy(source.cp2);
2459
+ this.p2.copy(source.p2);
2460
+ return this;
2461
+ }
2462
+ }
2463
+
2464
+ class EllipseCurve extends RoundCurve {
2465
+ constructor(cx = 0, cy = 0, rx = 1, ry = 1, rotate = 0, startAngle = 0, endAngle = Math.PI * 2, clockwise = false) {
2466
+ super(
2467
+ new Vector2(cx, cy),
2468
+ new Vector2(rx, ry),
2469
+ new Vector2(),
2470
+ rotate,
2471
+ startAngle,
2472
+ endAngle,
2473
+ clockwise
2474
+ );
2475
+ }
2476
+ drawTo(ctx) {
2477
+ ctx.ellipse(
2478
+ this.cx,
2479
+ this.cy,
2480
+ this.rx,
2481
+ this.ry,
2482
+ this.rotate,
2483
+ this.startAngle,
2484
+ this.endAngle,
2485
+ !this.clockwise
2486
+ );
2487
+ return this;
2488
+ }
1605
2489
  }
1606
2490
 
1607
2491
  class LineCurve extends Curve {
1608
- constructor(start = new Vector2(), end = new Vector2()) {
2492
+ constructor(p1, p2) {
1609
2493
  super();
1610
- this.start = start;
1611
- this.end = end;
2494
+ this.p1 = p1;
2495
+ this.p2 = p2;
2496
+ }
2497
+ static from(p1x, p1y, p2x, p2y) {
2498
+ return new LineCurve(
2499
+ new Vector2(p1x, p1y),
2500
+ new Vector2(p2x, p2y)
2501
+ );
1612
2502
  }
1613
2503
  getPoint(t, output = new Vector2()) {
1614
2504
  if (t === 1) {
1615
- output.copy(this.end);
2505
+ output.copy(this.p2);
1616
2506
  } else {
1617
- output.copy(this.end).sub(this.start).scale(t).add(this.start);
2507
+ output.copy(this.p2).sub(this.p1).scale(t).add(this.p1);
1618
2508
  }
1619
2509
  return output;
1620
2510
  }
@@ -1622,330 +2512,247 @@ class LineCurve extends Curve {
1622
2512
  return this.getPoint(u, output);
1623
2513
  }
1624
2514
  getTangent(_t, output = new Vector2()) {
1625
- return output.subVectors(this.end, this.start).normalize();
2515
+ return output.subVectors(this.p2, this.p1).normalize();
1626
2516
  }
1627
2517
  getTangentAt(u, output = new Vector2()) {
1628
2518
  return this.getTangent(u, output);
1629
2519
  }
1630
- getControlPoints() {
1631
- return [
1632
- this.start,
1633
- this.end
1634
- ];
2520
+ getControlPointRefs() {
2521
+ return [this.p1, this.p2];
2522
+ }
2523
+ getAdaptivePointArray(output = []) {
2524
+ output.push(
2525
+ this.p1.x,
2526
+ this.p1.y,
2527
+ this.p2.x,
2528
+ this.p2.y
2529
+ );
2530
+ return output;
1635
2531
  }
1636
2532
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1637
- const { start, end } = this;
1638
- min.x = Math.min(min.x, start.x, end.x);
1639
- min.y = Math.min(min.y, start.y, end.y);
1640
- max.x = Math.max(max.x, start.x, end.x);
1641
- max.y = Math.max(max.y, start.y, end.y);
2533
+ const { p1, p2 } = this;
2534
+ min.x = Math.min(min.x, p1.x, p2.x);
2535
+ min.y = Math.min(min.y, p1.y, p2.y);
2536
+ max.x = Math.max(max.x, p1.x, p2.x);
2537
+ max.y = Math.max(max.y, p1.y, p2.y);
1642
2538
  return { min, max };
1643
2539
  }
1644
2540
  toCommands() {
1645
- const { start, end } = this;
2541
+ const { p1, p2 } = this;
1646
2542
  return [
1647
- { type: "M", x: start.x, y: start.y },
1648
- { type: "L", x: end.x, y: end.y }
2543
+ { type: "M", x: p1.x, y: p1.y },
2544
+ { type: "L", x: p2.x, y: p2.y }
1649
2545
  ];
1650
2546
  }
1651
2547
  drawTo(ctx) {
1652
- const { start, end } = this;
1653
- ctx.lineTo(start.x, start.y);
1654
- ctx.lineTo(end.x, end.y);
2548
+ const { p1, p2 } = this;
2549
+ ctx.lineTo(p1.x, p1.y);
2550
+ ctx.lineTo(p2.x, p2.y);
1655
2551
  return this;
1656
2552
  }
1657
2553
  copy(source) {
1658
2554
  super.copy(source);
1659
- this.start.copy(source.start);
1660
- this.end.copy(source.end);
2555
+ this.p1.copy(source.p1);
2556
+ this.p2.copy(source.p2);
1661
2557
  return this;
1662
2558
  }
1663
2559
  }
1664
2560
 
1665
- class HeartCurve extends Curve {
1666
- constructor(center, size, start = 0, end = 1) {
1667
- super();
1668
- this.center = center;
1669
- this.size = size;
1670
- this.start = start;
1671
- this.end = end;
1672
- this.update();
1673
- }
1674
- curveT = 0;
1675
- update() {
1676
- const { x, y } = this.center;
1677
- const A = new Vector2(x + 0.5 * this.size, y - 0.5 * this.size);
1678
- const t = new Vector2(x - 0.5 * this.size, y - 0.5 * this.size);
1679
- const i = new Vector2(x, y + 0.5 * this.size);
1680
- const curve1 = new CircleCurve(A, Math.SQRT1_2 * this.size, -0.25 * Math.PI, 0.75 * Math.PI);
1681
- const curve5 = new CircleCurve(t, Math.SQRT1_2 * this.size, -0.75 * Math.PI, 0.25 * Math.PI);
1682
- const curve3 = new CircleCurve(i, 0.5 * Math.SQRT1_2 * this.size, 0.75 * Math.PI, 1.25 * Math.PI);
1683
- const e = new Vector2(x, y + this.size);
1684
- const l = new Vector2(x + this.size, y);
1685
- const c = new Vector2().lerpVectors(l, e, 0.75);
1686
- const h = new Vector2(x - this.size, y);
1687
- const a = new Vector2().lerpVectors(h, e, 0.75);
1688
- const curve2 = new LineCurve(l, c);
1689
- const curve4 = new LineCurve(a, h);
1690
- this.curves = [curve1, curve2, curve3, curve4, curve5];
1691
- return this;
1692
- }
1693
- getPoint(t) {
1694
- return this.getCurve(t).getPoint(this.curveT);
1695
- }
1696
- getPointAt(t) {
1697
- return this.getPoint(t);
1698
- }
1699
- getCurve(t) {
1700
- let val = (t * (this.end - this.start) + this.start) % 1;
1701
- val < 0 && (val += 1);
1702
- val *= 9 * Math.PI / 8 + 1.5;
1703
- let index;
1704
- const PI_1_2 = 0.5 * Math.PI;
1705
- if (val < PI_1_2) {
1706
- index = 0;
1707
- this.curveT = val / PI_1_2;
1708
- } else if (val < PI_1_2 + 0.75) {
1709
- index = 1;
1710
- this.curveT = (val - PI_1_2) / 0.75;
1711
- } else if (val < 5 * Math.PI / 8 + 0.75) {
1712
- index = 2;
1713
- this.curveT = (val - PI_1_2 - 0.75) / (Math.PI / 8);
1714
- } else if (val < 5 * Math.PI / 8 + 1.5) {
1715
- index = 3;
1716
- this.curveT = (val - 5 * Math.PI / 8 - 0.75) / 0.75;
1717
- } else {
1718
- index = 4;
1719
- this.curveT = (val - 5 * Math.PI / 8 - 1.5) / PI_1_2;
1720
- }
1721
- return this.curves[index];
1722
- }
1723
- getTangent(t, output) {
1724
- return this.getCurve(t).getTangent(this.curveT, output);
1725
- }
1726
- getNormal(t, output) {
1727
- return this.getCurve(t).getNormal(this.curveT, output);
1728
- }
1729
- getControlPoints() {
1730
- return this.curves.flatMap((curve) => curve.getControlPoints());
1731
- }
1732
- getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1733
- this.curves.forEach((curve) => curve.getMinMax(min, max));
1734
- return { min, max };
1735
- }
1736
- toCommands() {
1737
- return this.curves.flatMap((curve) => curve.toCommands());
1738
- }
1739
- drawTo(ctx) {
1740
- this.curves.forEach((curve) => curve.drawTo(ctx));
1741
- return this;
1742
- }
2561
+ class PloygonCurve extends CompositeCurve {
2562
+ //
1743
2563
  }
1744
2564
 
1745
- class PloygonCurve extends Curve {
1746
- constructor(center, radius = 0, number = 0, start = 0, end = 1) {
1747
- super();
1748
- this.center = center;
1749
- this.radius = radius;
1750
- this.number = number;
1751
- this.start = start;
1752
- this.end = end;
1753
- this.update();
1754
- }
1755
- curves = [];
1756
- curveT = 0;
1757
- points = [];
1758
- update() {
1759
- for (let i = 0; i < this.number; i++) {
1760
- let radian = i * 2 * Math.PI / this.number;
1761
- radian -= 0.5 * Math.PI;
1762
- this.points.push(
2565
+ class EquilateralPloygonCurve extends PloygonCurve {
2566
+ constructor(cx = 0, cy = 0, radius = 1, sideCount = 3) {
2567
+ const points = [];
2568
+ for (let i = 0; i < sideCount; i++) {
2569
+ const radian = i * 2 * Math.PI / sideCount - 0.5 * Math.PI;
2570
+ points.push(
1763
2571
  new Vector2(
1764
- this.radius * Math.cos(radian),
1765
- this.radius * Math.sin(radian)
1766
- ).add(this.center)
2572
+ radius * Math.cos(radian),
2573
+ radius * Math.sin(radian)
2574
+ ).add({ x: cx, y: cy })
1767
2575
  );
1768
2576
  }
1769
- for (let i = 0; i < this.number; i++) {
1770
- this.curves.push(new LineCurve(this.points[i], this.points[(i + 1) % this.number]));
2577
+ const curves = [];
2578
+ for (let i = 0; i < sideCount; i++) {
2579
+ curves.push(
2580
+ new LineCurve(
2581
+ points[i],
2582
+ points[(i + 1) % sideCount]
2583
+ )
2584
+ );
1771
2585
  }
1772
- return this;
1773
- }
1774
- getCurve(t) {
1775
- let pos = (t * (this.end - this.start) + this.start) % 1;
1776
- pos < 0 && (pos += 1);
1777
- const v = pos * this.number;
1778
- const index = Math.floor(v);
1779
- this.curveT = v - index;
1780
- return this.curves[index];
1781
- }
1782
- getPoint(t, output) {
1783
- return this.getCurve(t).getPoint(this.curveT, output);
1784
- }
1785
- getPointAt(u, output) {
1786
- return this.getPoint(u, output);
1787
- }
1788
- getTangent(t, output) {
1789
- return this.getCurve(t).getTangent(this.curveT, output);
1790
- }
1791
- getNormal(t, output) {
1792
- return this.getCurve(t).getNormal(this.curveT, output);
1793
- }
1794
- getControlPoints() {
1795
- return this.curves.flatMap((curve) => curve.getControlPoints());
1796
- }
1797
- getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1798
- this.curves.forEach((curve) => curve.getMinMax(min, max));
1799
- return { min, max };
1800
- }
1801
- toCommands() {
1802
- return this.curves.flatMap((curve) => curve.toCommands());
1803
- }
1804
- drawTo(ctx) {
1805
- this.curves.forEach((curve) => curve.drawTo(ctx));
1806
- return this;
2586
+ super(curves);
2587
+ this.cx = cx;
2588
+ this.cy = cy;
2589
+ this.radius = radius;
2590
+ this.sideCount = sideCount;
1807
2591
  }
1808
2592
  }
1809
2593
 
1810
2594
  class QuadraticBezierCurve extends Curve {
1811
- constructor(start = new Vector2(), control = new Vector2(), end = new Vector2()) {
2595
+ constructor(p1, cp, p2) {
1812
2596
  super();
1813
- this.start = start;
1814
- this.control = control;
1815
- this.end = end;
2597
+ this.p1 = p1;
2598
+ this.cp = cp;
2599
+ this.p2 = p2;
2600
+ }
2601
+ static from(p1x, p1y, cpx, cpy, p2x, p2y) {
2602
+ return new QuadraticBezierCurve(
2603
+ new Vector2(p1x, p1y),
2604
+ new Vector2(cpx, cpy),
2605
+ new Vector2(p2x, p2y)
2606
+ );
1816
2607
  }
1817
2608
  getPoint(t, output = new Vector2()) {
1818
- const { start, control, end } = this;
2609
+ const { p1, cp, p2 } = this;
1819
2610
  output.set(
1820
- quadraticBezier(t, start.x, control.x, end.x),
1821
- quadraticBezier(t, start.y, control.y, end.y)
2611
+ quadraticBezier(t, p1.x, cp.x, p2.x),
2612
+ quadraticBezier(t, p1.y, cp.y, p2.y)
1822
2613
  );
1823
2614
  return output;
1824
2615
  }
1825
- getControlPoints() {
1826
- return [
1827
- this.start,
1828
- this.control,
1829
- this.end
1830
- ];
2616
+ getControlPointRefs() {
2617
+ return [this.p1, this.cp, this.p2];
2618
+ }
2619
+ getAdaptivePointArray(output = []) {
2620
+ return getAdaptiveQuadraticBezierCurvePoints(
2621
+ this.p1.x,
2622
+ this.p1.y,
2623
+ this.cp.x,
2624
+ this.cp.y,
2625
+ this.p2.x,
2626
+ this.p2.y,
2627
+ 0.5,
2628
+ output
2629
+ );
1831
2630
  }
1832
2631
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1833
- const { start, control, end } = this;
1834
- const x1 = 0.5 * (start.x + control.x);
1835
- const y1 = 0.5 * (start.y + control.y);
1836
- const x2 = 0.5 * (start.x + end.x);
1837
- const y2 = 0.5 * (start.y + end.y);
1838
- min.x = Math.min(min.x, start.x, end.x, x1, x2);
1839
- min.y = Math.min(min.y, start.y, end.y, y1, y2);
1840
- max.x = Math.max(max.x, start.x, end.x, x1, x2);
1841
- max.y = Math.max(max.y, start.y, end.y, y1, y2);
2632
+ const { p1, cp, p2 } = this;
2633
+ const x1 = 0.5 * (p1.x + cp.x);
2634
+ const y1 = 0.5 * (p1.y + cp.y);
2635
+ const x2 = 0.5 * (p1.x + p2.x);
2636
+ const y2 = 0.5 * (p1.y + p2.y);
2637
+ min.x = Math.min(min.x, p1.x, p2.x, x1, x2);
2638
+ min.y = Math.min(min.y, p1.y, p2.y, y1, y2);
2639
+ max.x = Math.max(max.x, p1.x, p2.x, x1, x2);
2640
+ max.y = Math.max(max.y, p1.y, p2.y, y1, y2);
1842
2641
  return { min, max };
1843
2642
  }
1844
2643
  toCommands() {
1845
- const { start, control, end } = this;
2644
+ const { p1, cp, p2 } = this;
1846
2645
  return [
1847
- { type: "M", x: start.x, y: start.y },
1848
- { type: "Q", x1: control.x, y1: control.y, x: end.x, y: end.y }
2646
+ { type: "M", x: p1.x, y: p1.y },
2647
+ { type: "Q", x1: cp.x, y1: cp.y, x: p2.x, y: p2.y }
1849
2648
  ];
1850
2649
  }
1851
2650
  drawTo(ctx) {
1852
- const { start, control, end } = this;
1853
- ctx.lineTo(start.x, start.y);
1854
- ctx.quadraticCurveTo(control.x, control.y, end.x, end.y);
2651
+ const { p1, cp, p2 } = this;
2652
+ ctx.lineTo(p1.x, p1.y);
2653
+ ctx.quadraticCurveTo(cp.x, cp.y, p2.x, p2.y);
1855
2654
  return this;
1856
2655
  }
1857
2656
  copy(source) {
1858
2657
  super.copy(source);
1859
- this.start.copy(source.start);
1860
- this.control.copy(source.control);
1861
- this.end.copy(source.end);
2658
+ this.p1.copy(source.p1);
2659
+ this.cp.copy(source.cp);
2660
+ this.p2.copy(source.p2);
1862
2661
  return this;
1863
2662
  }
1864
2663
  }
1865
2664
 
1866
- class RectangularCurve extends Curve {
1867
- constructor(center, rx, aspectRatio = 1, start = 0, end = 1) {
1868
- super();
1869
- this.center = center;
1870
- this.rx = rx;
1871
- this.aspectRatio = aspectRatio;
1872
- this.start = start;
1873
- this.end = end;
1874
- this.update();
1875
- }
1876
- curves = [];
1877
- curveT = 0;
1878
- get x() {
1879
- return this.center.x - this.rx;
1880
- }
1881
- get y() {
1882
- return this.center.y - this.rx / this.aspectRatio;
1883
- }
1884
- get width() {
1885
- return this.rx * 2;
1886
- }
1887
- get height() {
1888
- return this.rx / this.aspectRatio * 2;
1889
- }
1890
- update() {
1891
- const { x, y } = this.center;
1892
- const offsetX = this.rx;
1893
- const offsetY = this.rx / this.aspectRatio;
2665
+ class RectangleCurve extends PloygonCurve {
2666
+ constructor(x = 0, y = 0, width = 0, height = 0) {
1894
2667
  const points = [
1895
- new Vector2(x - offsetX, y - offsetY),
1896
- new Vector2(x + offsetX, y - offsetY),
1897
- new Vector2(x + offsetX, y + offsetY),
1898
- new Vector2(x - offsetX, y + offsetY)
2668
+ new Vector2(x, y),
2669
+ new Vector2(x + width, y),
2670
+ new Vector2(x + width, y + height),
2671
+ new Vector2(x, y + height)
1899
2672
  ];
1900
- for (let i = 0; i < 4; i++) {
1901
- this.curves.push(new LineCurve(points[i].clone(), points[(i + 1) % 4].clone()));
1902
- }
1903
- return this;
1904
- }
1905
- getCurve(t) {
1906
- let current = (t * (this.end - this.start) + this.start) % 1;
1907
- current < 0 && (current += 1);
1908
- current *= (1 + this.aspectRatio) * 2;
1909
- let i;
1910
- if (current < this.aspectRatio) {
1911
- i = 0;
1912
- this.curveT = current / this.aspectRatio;
1913
- } else if (current < this.aspectRatio + 1) {
1914
- i = 1;
1915
- this.curveT = (current - this.aspectRatio) / 1;
1916
- } else if (current < 2 * this.aspectRatio + 1) {
1917
- i = 2;
1918
- this.curveT = (current - this.aspectRatio - 1) / this.aspectRatio;
1919
- } else {
1920
- i = 3;
1921
- this.curveT = (current - 2 * this.aspectRatio - 1) / 1;
1922
- }
1923
- return this.curves[i];
1924
- }
1925
- getPoint(t, output) {
1926
- return this.getCurve(t).getPoint(this.curveT, output);
1927
- }
1928
- getPointAt(u, output) {
1929
- return this.getPoint(u, output);
1930
- }
1931
- getTangent(t, output) {
1932
- return this.getCurve(t).getTangent(this.curveT, output);
1933
- }
1934
- getNormal(t, output) {
1935
- return this.getCurve(t).getNormal(this.curveT, output);
1936
- }
1937
- getControlPoints() {
1938
- return this.curves.flatMap((curve) => curve.getControlPoints());
2673
+ super([
2674
+ new LineCurve(points[0], points[1]),
2675
+ new LineCurve(points[1], points[2]),
2676
+ new LineCurve(points[2], points[3]),
2677
+ new LineCurve(points[3], points[0])
2678
+ ]);
2679
+ this.x = x;
2680
+ this.y = y;
2681
+ this.width = width;
2682
+ this.height = height;
1939
2683
  }
1940
- getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1941
- this.curves.forEach((curve) => curve.getMinMax(min, max));
1942
- return { min, max };
2684
+ drawTo(ctx) {
2685
+ ctx.rect(this.x, this.y, this.width, this.height);
2686
+ return this;
2687
+ }
2688
+ fillTriangulate(options = {}) {
2689
+ let {
2690
+ vertices = [],
2691
+ indices = [],
2692
+ verticesStride = 2,
2693
+ verticesOffset = 0,
2694
+ indicesOffset = 0
2695
+ } = options;
2696
+ const { x, y, width, height } = this;
2697
+ const points = [
2698
+ x,
2699
+ y,
2700
+ x + width,
2701
+ y,
2702
+ x + width,
2703
+ y + height,
2704
+ x,
2705
+ y + height
2706
+ ];
2707
+ let count = 0;
2708
+ verticesOffset *= verticesStride;
2709
+ vertices[verticesOffset + count] = points[0];
2710
+ vertices[verticesOffset + count + 1] = points[1];
2711
+ count += verticesStride;
2712
+ vertices[verticesOffset + count] = points[2];
2713
+ vertices[verticesOffset + count + 1] = points[3];
2714
+ count += verticesStride;
2715
+ vertices[verticesOffset + count] = points[6];
2716
+ vertices[verticesOffset + count + 1] = points[7];
2717
+ count += verticesStride;
2718
+ vertices[verticesOffset + count] = points[4];
2719
+ vertices[verticesOffset + count + 1] = points[5];
2720
+ count += verticesStride;
2721
+ const verticesIndex = verticesOffset / verticesStride;
2722
+ indices[indicesOffset++] = verticesIndex;
2723
+ indices[indicesOffset++] = verticesIndex + 1;
2724
+ indices[indicesOffset++] = verticesIndex + 2;
2725
+ indices[indicesOffset++] = verticesIndex + 1;
2726
+ indices[indicesOffset++] = verticesIndex + 3;
2727
+ indices[indicesOffset++] = verticesIndex + 2;
2728
+ return { vertices, indices };
1943
2729
  }
1944
- toCommands() {
1945
- return this.curves.flatMap((curve) => curve.toCommands());
2730
+ }
2731
+
2732
+ class RoundRectangleCurve extends RoundCurve {
2733
+ constructor(x = 0, y = 0, width = 1, height = 1, radius = 1) {
2734
+ const halfWidth = width / 2;
2735
+ const halfHeight = height / 2;
2736
+ const cx = x + halfWidth;
2737
+ const cy = y + halfHeight;
2738
+ const rx = Math.max(0, Math.min(radius, Math.min(halfWidth, halfHeight)));
2739
+ const ry = rx;
2740
+ const dx = halfWidth - rx;
2741
+ const dy = halfHeight - ry;
2742
+ super(
2743
+ new Vector2(cx, cy),
2744
+ new Vector2(rx, ry),
2745
+ new Vector2(dx, dy)
2746
+ );
2747
+ this.x = x;
2748
+ this.y = y;
2749
+ this.width = width;
2750
+ this.height = height;
2751
+ this.radius = radius;
1946
2752
  }
1947
2753
  drawTo(ctx) {
1948
- this.curves.forEach((curve) => curve.drawTo(ctx));
2754
+ const { x, y, width, height, radius } = this;
2755
+ ctx.roundRect(x, y, width, height, radius);
1949
2756
  return this;
1950
2757
  }
1951
2758
  }
@@ -1970,7 +2777,7 @@ class SplineCurve extends Curve {
1970
2777
  );
1971
2778
  return output;
1972
2779
  }
1973
- getControlPoints() {
2780
+ getControlPointRefs() {
1974
2781
  return this.points;
1975
2782
  }
1976
2783
  copy(source) {
@@ -1983,22 +2790,16 @@ class SplineCurve extends Curve {
1983
2790
  }
1984
2791
  }
1985
2792
 
1986
- class CurvePath extends Curve {
1987
- curves = [];
2793
+ class CurvePath extends CompositeCurve {
1988
2794
  startPoint;
1989
2795
  currentPoint;
1990
2796
  autoClose = false;
1991
- _cacheLengths = [];
1992
2797
  constructor(points) {
1993
2798
  super();
1994
2799
  if (points) {
1995
2800
  this.addPoints(points);
1996
2801
  }
1997
2802
  }
1998
- addCurve(curve) {
1999
- this.curves.push(curve);
2000
- return this;
2001
- }
2002
2803
  addPoints(points) {
2003
2804
  this.moveTo(points[0].x, points[0].y);
2004
2805
  for (let i = 1, len = points.length; i < len; i++) {
@@ -2008,82 +2809,26 @@ class CurvePath extends Curve {
2008
2809
  return this;
2009
2810
  }
2010
2811
  addCommands(commands) {
2011
- addPathCommandsToPath2D(commands, this);
2812
+ svgPathCommandsAddToPath2D(commands, this);
2012
2813
  return this;
2013
2814
  }
2014
2815
  addData(data) {
2015
- this.addCommands(pathDataToPathCommands(data));
2816
+ this.addCommands(svgPathDataToCommands(data));
2016
2817
  return this;
2017
2818
  }
2018
- getPoint(t, output = new Vector2()) {
2019
- const d = t * this.getLength();
2020
- const curveLengths = this.getCurveLengths();
2021
- let i = 0;
2022
- while (i < curveLengths.length) {
2023
- if (curveLengths[i] >= d) {
2024
- const diff = curveLengths[i] - d;
2025
- const curve = this.curves[i];
2026
- const segmentLength = curve.getLength();
2027
- return curve.getPointAt(segmentLength === 0 ? 0 : 1 - diff / segmentLength, output);
2028
- }
2029
- i++;
2819
+ getUnevenPointArray(count = 40, output = []) {
2820
+ super.getUnevenPointArray(count, output);
2821
+ if (this.autoClose && output.length >= 4 && (output[0] !== output[output.length - 2] && output[1] !== output[output.length - 1])) {
2822
+ output.push(output[0], output[1]);
2030
2823
  }
2031
2824
  return output;
2032
2825
  }
2033
- getControlPoints() {
2034
- return this.curves.flatMap((curve) => curve.getControlPoints());
2035
- }
2036
- getLength() {
2037
- const lengths = this.getCurveLengths();
2038
- return lengths[lengths.length - 1];
2039
- }
2040
- updateArcLengths() {
2041
- super.updateArcLengths();
2042
- this._cacheLengths = [];
2043
- this.getCurveLengths();
2044
- }
2045
- getCurveLengths() {
2046
- if (this._cacheLengths.length === this.curves.length) {
2047
- return this._cacheLengths;
2048
- }
2049
- const lengths = [];
2050
- let sums = 0;
2051
- for (let i = 0, l = this.curves.length; i < l; i++) {
2052
- sums += this.curves[i].getLength();
2053
- lengths.push(sums);
2054
- }
2055
- this._cacheLengths = lengths;
2056
- return lengths;
2057
- }
2058
- getSpacedPoints(divisions = 40) {
2059
- const points = [];
2060
- for (let i = 0; i <= divisions; i++) {
2061
- points.push(this.getPoint(i / divisions));
2062
- }
2063
- if (this.autoClose) {
2064
- points.push(points[0]);
2065
- }
2066
- return points;
2067
- }
2068
- getPoints(divisions = 12) {
2069
- const points = [];
2070
- const curves = this.curves;
2071
- let last;
2072
- for (let i = 0, len = curves.length; i < len; i++) {
2073
- const curve = curves[i];
2074
- const curvePoints = curve.getPoints(divisions);
2075
- for (let i2 = 0; i2 < curvePoints.length; i2++) {
2076
- const point = curvePoints[i2];
2077
- if (last?.equals(point))
2078
- continue;
2079
- points.push(point);
2080
- last = point;
2081
- }
2826
+ getSpacedPointArray(count = 40, output = []) {
2827
+ super.getSpacedPointArray(count, output);
2828
+ if (this.autoClose && output.length >= 4 && (output[0] !== output[output.length - 2] && output[1] !== output[output.length - 1])) {
2829
+ output.push(output[0], output[1]);
2082
2830
  }
2083
- if (this.autoClose && points.length > 1 && !points[points.length - 1].equals(points[0])) {
2084
- points.push(points[0]);
2085
- }
2086
- return points;
2831
+ return output;
2087
2832
  }
2088
2833
  _setCurrentPoint(point) {
2089
2834
  this.currentPoint = new Vector2(point.x, point.y);
@@ -2092,12 +2837,21 @@ class CurvePath extends Curve {
2092
2837
  }
2093
2838
  return this;
2094
2839
  }
2840
+ _connetLineTo(curve) {
2841
+ if (this.curves.length > 0) {
2842
+ const first = curve.getPoint(0);
2843
+ if (!this.currentPoint || !first.equals(this.currentPoint)) {
2844
+ this.lineTo(first.x, first.y);
2845
+ }
2846
+ }
2847
+ return this;
2848
+ }
2095
2849
  closePath() {
2096
2850
  const start = this.startPoint;
2097
2851
  if (start) {
2098
2852
  const end = this.currentPoint;
2099
2853
  if (end && !start.equals(end)) {
2100
- this.curves.push(new LineCurve(end.clone(), start));
2854
+ this.curves.push(new LineCurve(end, start));
2101
2855
  end.copy(start);
2102
2856
  }
2103
2857
  this.startPoint = void 0;
@@ -2113,9 +2867,11 @@ class CurvePath extends Curve {
2113
2867
  const start = this.currentPoint;
2114
2868
  if (!start?.equals({ x, y })) {
2115
2869
  this.curves.push(
2116
- new LineCurve(
2117
- start?.clone() ?? new Vector2(),
2118
- new Vector2(x, y)
2870
+ LineCurve.from(
2871
+ start?.x ?? 0,
2872
+ start?.y ?? 0,
2873
+ x,
2874
+ y
2119
2875
  )
2120
2876
  );
2121
2877
  }
@@ -2126,11 +2882,15 @@ class CurvePath extends Curve {
2126
2882
  const start = this.currentPoint;
2127
2883
  if (!start?.equals({ x, y })) {
2128
2884
  this.curves.push(
2129
- new CubicBezierCurve(
2130
- start?.clone() ?? new Vector2(),
2131
- new Vector2(cp1x, cp1y),
2132
- new Vector2(cp2x, cp2y),
2133
- new Vector2(x, y)
2885
+ CubicBezierCurve.from(
2886
+ start?.x ?? 0,
2887
+ start?.y ?? 0,
2888
+ cp1x,
2889
+ cp1y,
2890
+ cp2x,
2891
+ cp2y,
2892
+ x,
2893
+ y
2134
2894
  )
2135
2895
  );
2136
2896
  }
@@ -2141,10 +2901,13 @@ class CurvePath extends Curve {
2141
2901
  const start = this.currentPoint;
2142
2902
  if (!start?.equals({ x, y })) {
2143
2903
  this.curves.push(
2144
- new QuadraticBezierCurve(
2145
- start?.clone() ?? new Vector2(),
2146
- new Vector2(cpx, cpy),
2147
- new Vector2(x, y)
2904
+ QuadraticBezierCurve.from(
2905
+ start?.x ?? 0,
2906
+ start?.y ?? 0,
2907
+ cpx,
2908
+ cpy,
2909
+ x,
2910
+ y
2148
2911
  )
2149
2912
  );
2150
2913
  }
@@ -2152,12 +2915,23 @@ class CurvePath extends Curve {
2152
2915
  return this;
2153
2916
  }
2154
2917
  arc(x, y, radius, startAngle, endAngle, counterclockwise) {
2155
- this.ellipse(x, y, radius, radius, 0, startAngle, endAngle, counterclockwise);
2918
+ const curve = new ArcCurve(
2919
+ x,
2920
+ y,
2921
+ radius,
2922
+ startAngle,
2923
+ endAngle,
2924
+ !counterclockwise
2925
+ );
2926
+ this._connetLineTo(curve);
2927
+ this.curves.push(curve);
2928
+ this._setCurrentPoint(curve.getPoint(1));
2156
2929
  return this;
2157
2930
  }
2158
2931
  relativeArc(x, y, radius, startAngle, endAngle, counterclockwise) {
2159
- const point = this.currentPoint ?? new Vector2();
2160
- this.arc(x + point.x, y + point.y, radius, startAngle, endAngle, counterclockwise);
2932
+ x += this.currentPoint?.x ?? 0;
2933
+ y += this.currentPoint?.y ?? 0;
2934
+ this.arc(x, y, radius, startAngle, endAngle, counterclockwise);
2161
2935
  return this;
2162
2936
  }
2163
2937
  // TODO
@@ -2168,7 +2942,8 @@ class CurvePath extends Curve {
2168
2942
  }
2169
2943
  ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise = true) {
2170
2944
  const curve = new EllipseCurve(
2171
- new Vector2(x, y),
2945
+ x,
2946
+ y,
2172
2947
  radiusX,
2173
2948
  radiusY,
2174
2949
  rotation,
@@ -2176,29 +2951,28 @@ class CurvePath extends Curve {
2176
2951
  endAngle,
2177
2952
  !counterclockwise
2178
2953
  );
2179
- if (this.curves.length > 0) {
2180
- const first = curve.getPoint(0);
2181
- if (!this.currentPoint || !first.equals(this.currentPoint)) {
2182
- this.lineTo(first.x, first.y);
2183
- }
2184
- }
2954
+ this._connetLineTo(curve);
2185
2955
  this.curves.push(curve);
2186
2956
  this._setCurrentPoint(curve.getPoint(1));
2187
2957
  return this;
2188
2958
  }
2189
2959
  relativeEllipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) {
2190
- const point = this.currentPoint ?? new Vector2();
2191
- this.ellipse(x + point.x, y + point.y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise);
2960
+ x += this.currentPoint?.x ?? 0;
2961
+ y += this.currentPoint?.y ?? 0;
2962
+ this.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise);
2192
2963
  return this;
2193
2964
  }
2194
- rect(x, y, w, h) {
2195
- this.curves.push(
2196
- new RectangularCurve(
2197
- new Vector2(x + w / 2, y + h / 2),
2198
- w / 2,
2199
- w / h
2200
- )
2201
- );
2965
+ rect(x, y, width, height) {
2966
+ const curve = new RectangleCurve(x, y, width, height);
2967
+ this._connetLineTo(curve);
2968
+ this.curves.push(curve);
2969
+ this._setCurrentPoint({ x, y });
2970
+ return this;
2971
+ }
2972
+ roundRect(x, y, width, height, radii) {
2973
+ const curve = new RoundRectangleCurve(x, y, width, height, radii);
2974
+ this._connetLineTo(curve);
2975
+ this.curves.push(curve);
2202
2976
  this._setCurrentPoint({ x, y });
2203
2977
  return this;
2204
2978
  }
@@ -2208,17 +2982,6 @@ class CurvePath extends Curve {
2208
2982
  this._setCurrentPoint(points[points.length - 1]);
2209
2983
  return this;
2210
2984
  }
2211
- getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
2212
- this.curves.forEach((curve) => curve.getMinMax(min, max));
2213
- return { min, max };
2214
- }
2215
- getBoundingBox() {
2216
- const { min, max } = this.getMinMax();
2217
- return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
2218
- }
2219
- toCommands() {
2220
- return this.curves.flatMap((curve) => curve.toCommands());
2221
- }
2222
2985
  drawTo(ctx) {
2223
2986
  const point = this.curves[0]?.getPoint(0);
2224
2987
  if (point) {
@@ -2269,20 +3032,22 @@ function getIntersectionPoint(p1, p2, q1, q2) {
2269
3032
  );
2270
3033
  }
2271
3034
 
2272
- class Path2D {
2273
- currentPath = new CurvePath();
2274
- paths = [this.currentPath];
3035
+ class Path2D extends CompositeCurve {
3036
+ currentCurve = new CurvePath();
2275
3037
  style;
2276
3038
  get startPoint() {
2277
- return this.currentPath.startPoint;
3039
+ return this.currentCurve.startPoint;
2278
3040
  }
2279
3041
  get currentPoint() {
2280
- return this.currentPath.currentPoint;
3042
+ return this.currentCurve.currentPoint;
2281
3043
  }
2282
3044
  get strokeWidth() {
2283
3045
  return this.style.strokeWidth ?? ((this.style.stroke ?? "none") === "none" ? 0 : 1);
2284
3046
  }
2285
3047
  constructor(path, style = {}) {
3048
+ super();
3049
+ this.curves.push(this.currentCurve);
3050
+ this.style = style;
2286
3051
  if (path) {
2287
3052
  if (path instanceof Path2D) {
2288
3053
  this.addPath(path);
@@ -2292,99 +3057,94 @@ class Path2D {
2292
3057
  this.addData(path);
2293
3058
  }
2294
3059
  }
2295
- this.style = style;
2296
3060
  }
2297
3061
  addPath(path) {
2298
3062
  if (path instanceof Path2D) {
2299
- this.paths.push(...path.paths.map((v) => v.clone()));
3063
+ this.curves.push(...path.curves.map((v) => v.clone()));
2300
3064
  } else {
2301
- this.paths.push(path);
3065
+ this.curves.push(path);
2302
3066
  }
2303
3067
  return this;
2304
3068
  }
2305
3069
  closePath() {
2306
3070
  const startPoint = this.startPoint;
2307
3071
  if (startPoint) {
2308
- this.currentPath.closePath();
2309
- if (this.currentPath.curves.length > 0) {
2310
- this.currentPath = new CurvePath().moveTo(startPoint.x, startPoint.y);
2311
- this.paths.push(this.currentPath);
3072
+ this.currentCurve.closePath();
3073
+ if (this.currentCurve.curves.length > 0) {
3074
+ this.currentCurve = new CurvePath().moveTo(startPoint.x, startPoint.y);
3075
+ this.curves.push(this.currentCurve);
2312
3076
  }
2313
3077
  }
2314
3078
  return this;
2315
3079
  }
2316
3080
  moveTo(x, y) {
2317
- const { currentPoint, curves } = this.currentPath;
2318
- if (!currentPoint?.equals({ x, y })) {
2319
- if (curves.length) {
2320
- this.currentPath = new CurvePath().moveTo(x, y);
2321
- this.paths.push(this.currentPath);
2322
- } else {
2323
- this.currentPath.moveTo(x, y);
3081
+ if (!this.currentCurve.currentPoint?.equals({ x, y })) {
3082
+ if (this.currentCurve.curves.length) {
3083
+ this.currentCurve = new CurvePath();
3084
+ this.curves.push(this.currentCurve);
2324
3085
  }
3086
+ this.currentCurve.moveTo(x, y);
2325
3087
  }
2326
3088
  return this;
2327
3089
  }
2328
3090
  lineTo(x, y) {
2329
- this.currentPath.lineTo(x, y);
3091
+ this.currentCurve.lineTo(x, y);
2330
3092
  return this;
2331
3093
  }
2332
3094
  bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
2333
- this.currentPath.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
3095
+ this.currentCurve.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
2334
3096
  return this;
2335
3097
  }
2336
3098
  quadraticCurveTo(cpx, cpy, x, y) {
2337
- this.currentPath.quadraticCurveTo(cpx, cpy, x, y);
3099
+ this.currentCurve.quadraticCurveTo(cpx, cpy, x, y);
2338
3100
  return this;
2339
3101
  }
2340
3102
  arc(x, y, radius, startAngle, endAngle, counterclockwise) {
2341
- this.currentPath.arc(x, y, radius, startAngle, endAngle, counterclockwise);
3103
+ this.currentCurve.arc(x, y, radius, startAngle, endAngle, counterclockwise);
2342
3104
  return this;
2343
3105
  }
2344
3106
  arcTo(x1, y1, x2, y2, radius) {
2345
- this.currentPath.arcTo(x1, y1, x2, y2, radius);
3107
+ this.currentCurve.arcTo(x1, y1, x2, y2, radius);
2346
3108
  return this;
2347
3109
  }
2348
3110
  ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) {
2349
- this.currentPath.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise);
3111
+ this.currentCurve.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise);
3112
+ return this;
3113
+ }
3114
+ rect(x, y, width, height) {
3115
+ this.currentCurve.rect(x, y, width, height);
2350
3116
  return this;
2351
3117
  }
2352
- rect(x, y, w, h) {
2353
- this.currentPath.rect(x, y, w, h);
3118
+ roundRect(x, y, width, height, radii) {
3119
+ this.currentCurve.roundRect(x, y, width, height, radii);
2354
3120
  return this;
2355
3121
  }
2356
3122
  addCommands(commands) {
2357
- addPathCommandsToPath2D(commands, this);
3123
+ svgPathCommandsAddToPath2D(commands, this);
2358
3124
  return this;
2359
3125
  }
2360
3126
  addData(data) {
2361
- this.addCommands(pathDataToPathCommands(data));
3127
+ this.addCommands(svgPathDataToCommands(data));
2362
3128
  return this;
2363
3129
  }
2364
3130
  splineThru(points) {
2365
- this.currentPath.splineThru(points);
3131
+ this.currentCurve.splineThru(points);
2366
3132
  return this;
2367
3133
  }
2368
- getControlPoints() {
2369
- return this.paths.flatMap((path) => path.getControlPoints());
2370
- }
2371
- getCurves() {
2372
- return this.paths.flatMap((path) => path.curves);
2373
- }
2374
3134
  scale(sx, sy = sx, target = { x: 0, y: 0 }) {
2375
- this.getControlPoints().forEach((point) => {
3135
+ this.getControlPointRefs().forEach((point) => {
2376
3136
  point.scale(sx, sy, target);
2377
3137
  });
2378
3138
  return this;
2379
3139
  }
2380
3140
  skew(ax, ay = 0, target = { x: 0, y: 0 }) {
2381
- this.getControlPoints().forEach((point) => {
3141
+ this.getControlPointRefs().forEach((point) => {
2382
3142
  point.skew(ax, ay, target);
2383
3143
  });
2384
3144
  return this;
2385
3145
  }
2386
3146
  rotate(a, target = { x: 0, y: 0 }) {
2387
- this.getControlPoints().forEach((point) => {
3147
+ this.getControlPointRefs().forEach((point) => {
2388
3148
  point.rotate(a, target);
2389
3149
  });
2390
3150
  return this;
@@ -2393,12 +3153,14 @@ class Path2D {
2393
3153
  if (b === 0) {
2394
3154
  return this;
2395
3155
  }
2396
- const curves = this.getCurves();
3156
+ const curves = this.curves;
2397
3157
  const _list = [];
2398
3158
  const _isClockwise = [];
2399
3159
  const _points = [];
2400
3160
  curves.forEach((curve, index) => {
2401
- const points = curve.getControlPoints();
3161
+ if (!curve.getLength())
3162
+ return;
3163
+ const points = curve.getControlPointRefs();
2402
3164
  const isClockwise = curve.isClockwise();
2403
3165
  _points[index] = points;
2404
3166
  _isClockwise[index] = isClockwise;
@@ -2420,6 +3182,8 @@ class Path2D {
2420
3182
  });
2421
3183
  });
2422
3184
  curves.forEach((curve, index) => {
3185
+ if (!curve.getLength())
3186
+ return;
2423
3187
  const isClockwise = _isClockwise[index];
2424
3188
  const points = _points[index];
2425
3189
  points.forEach((point) => {
@@ -2446,20 +3210,20 @@ class Path2D {
2446
3210
  });
2447
3211
  return this;
2448
3212
  }
2449
- matrix(matrix) {
2450
- this.getCurves().forEach((curve) => curve.matrix(matrix));
3213
+ applyTransform(transform) {
3214
+ this.curves.forEach((curve) => curve.applyTransform(transform));
2451
3215
  return this;
2452
3216
  }
2453
3217
  getMinMax(min = Vector2.MAX, max = Vector2.MIN, withStyle = true) {
2454
3218
  const strokeWidth = this.strokeWidth;
2455
- this.getCurves().forEach((curve) => {
3219
+ this.curves.forEach((curve) => {
2456
3220
  curve.getMinMax(min, max);
2457
3221
  if (withStyle) {
2458
3222
  if (strokeWidth > 1) {
2459
3223
  const halfStrokeWidth = strokeWidth / 2;
2460
3224
  const isClockwise = curve.isClockwise();
2461
3225
  const points = [];
2462
- for (let t = 0; t <= 1; t += 1 / curve.arcLengthDivisions) {
3226
+ for (let t = 0; t <= 1; t += 1 / curve.arcLengthDivision) {
2463
3227
  const point = curve.getPoint(t);
2464
3228
  const normal = curve.getNormal(t);
2465
3229
  const dist1 = normal.clone().scale(isClockwise ? halfStrokeWidth : -halfStrokeWidth);
@@ -2492,7 +3256,7 @@ class Path2D {
2492
3256
  ctx.beginPath();
2493
3257
  ctx.save();
2494
3258
  setCanvasContext(ctx, style);
2495
- this.paths.forEach((path) => {
3259
+ this.curves.forEach((path) => {
2496
3260
  path.drawTo(ctx);
2497
3261
  });
2498
3262
  if (fill !== "none") {
@@ -2510,9 +3274,8 @@ class Path2D {
2510
3274
  ctx.beginPath();
2511
3275
  ctx.save();
2512
3276
  setCanvasContext(ctx, style);
2513
- this.getControlPoints().forEach((point) => {
2514
- ctx.moveTo(point.x, point.y);
2515
- ctx.arc(point.x, point.y, 4, 0, Math.PI * 2);
3277
+ this.getControlPointRefs().forEach((point) => {
3278
+ drawPoint(ctx, point.x, point.y, { radius: 4 });
2516
3279
  });
2517
3280
  if (fill !== "none") {
2518
3281
  ctx.fill();
@@ -2524,10 +3287,10 @@ class Path2D {
2524
3287
  return this;
2525
3288
  }
2526
3289
  toCommands() {
2527
- return this.paths.flatMap((path) => path.toCommands());
3290
+ return this.curves.flatMap((v) => v.toCommands());
2528
3291
  }
2529
3292
  toData() {
2530
- return this.paths.map((path) => path.toData()).join(" ");
3293
+ return this.curves.filter((v) => v.curves.length).map((v) => v.toData()).join(" ");
2531
3294
  }
2532
3295
  toSVGPathString() {
2533
3296
  const style = {
@@ -2552,10 +3315,34 @@ class Path2D {
2552
3315
  }
2553
3316
  return `<path d="${this.toData()}" style="${cssText}"></path>`;
2554
3317
  }
3318
+ copy(source) {
3319
+ this.currentCurve = source.currentCurve.clone();
3320
+ this.curves = source.curves.map((path) => path.clone());
3321
+ this.style = { ...source.style };
3322
+ return this;
3323
+ }
3324
+ clone() {
3325
+ return new this.constructor().copy(this);
3326
+ }
3327
+ }
3328
+
3329
+ class Path2DSet {
3330
+ constructor(paths = []) {
3331
+ this.paths = paths;
3332
+ }
3333
+ getBoundingBox(withStyle = true) {
3334
+ if (!this.paths.length) {
3335
+ return void 0;
3336
+ }
3337
+ const min = Vector2.MAX;
3338
+ const max = Vector2.MIN;
3339
+ this.paths.forEach((path) => path.getMinMax(min, max, withStyle));
3340
+ return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
3341
+ }
2555
3342
  toSVGString() {
2556
3343
  const { x, y, width, height } = this.getBoundingBox();
2557
- const path = this.toSVGPathString();
2558
- return `<svg viewBox="${x} ${y} ${width} ${height}" width="${width}px" height="${height}px" xmlns="http://www.w3.org/2000/svg">${path}</svg>`;
3344
+ const content = this.paths.map((path) => path.toSVGPathString()).join("");
3345
+ return `<svg viewBox="${x} ${y} ${width} ${height}" width="${width}px" height="${height}px" xmlns="http://www.w3.org/2000/svg">${content}</svg>`;
2559
3346
  }
2560
3347
  toSVGUrl() {
2561
3348
  return `data:image/svg+xml;base64,${btoa(this.toSVGString())}`;
@@ -2575,18 +3362,38 @@ class Path2D {
2575
3362
  if (ctx) {
2576
3363
  ctx.scale(pixelRatio, pixelRatio);
2577
3364
  ctx.translate(-left, -top);
2578
- this.drawTo(ctx, style);
3365
+ this.paths.forEach((path) => {
3366
+ path.drawTo(ctx, style);
3367
+ });
2579
3368
  }
2580
3369
  return canvas;
2581
3370
  }
2582
- copy(source) {
2583
- this.currentPath = source.currentPath.clone();
2584
- this.paths = source.paths.map((path) => path.clone());
2585
- this.style = { ...source.style };
2586
- return this;
2587
- }
2588
- clone() {
2589
- return new this.constructor().copy(this);
3371
+ }
3372
+
3373
+ const dataUri = "data:image/svg+xml;";
3374
+ const base64DataUri = `${dataUri}base64,`;
3375
+ const utf8DataUri = `${dataUri}charset=utf8,`;
3376
+ function svgToDom(svg) {
3377
+ if (typeof svg === "string") {
3378
+ let xml;
3379
+ if (svg.startsWith(base64DataUri)) {
3380
+ svg = svg.substring(base64DataUri.length, svg.length);
3381
+ xml = atob(svg);
3382
+ } else if (svg.startsWith(utf8DataUri)) {
3383
+ svg = svg.substring(utf8DataUri.length, svg.length);
3384
+ xml = decodeURIComponent(svg);
3385
+ } else {
3386
+ xml = svg;
3387
+ }
3388
+ const doc = new DOMParser().parseFromString(xml, "text/xml");
3389
+ const error = doc.querySelector("parsererror");
3390
+ if (error) {
3391
+ throw new Error(`${error.textContent ?? "parser error"}
3392
+ ${xml}`);
3393
+ }
3394
+ return doc.documentElement;
3395
+ } else {
3396
+ return svg;
2590
3397
  }
2591
3398
  }
2592
3399
 
@@ -3067,7 +3874,7 @@ function parseNode(node, style, paths = [], stylesheets = {}) {
3067
3874
  const transformStack = [];
3068
3875
  const transform = getNodeTransform(node, currentTransform, transformStack);
3069
3876
  if (path) {
3070
- path.matrix(currentTransform);
3877
+ path.applyTransform(currentTransform);
3071
3878
  paths.push(path);
3072
3879
  path.style = style;
3073
3880
  }
@@ -3089,100 +3896,41 @@ function parseNode(node, style, paths = [], stylesheets = {}) {
3089
3896
  return paths;
3090
3897
  }
3091
3898
 
3092
- const dataUri = "data:image/svg+xml;";
3093
- const base64DataUri = `${dataUri}base64,`;
3094
- const utf8DataUri = `${dataUri}charset=utf8,`;
3095
- function parseSVGToDOM(svg) {
3096
- if (typeof svg === "string") {
3097
- let xml;
3098
- if (svg.startsWith(base64DataUri)) {
3099
- svg = svg.substring(base64DataUri.length, svg.length);
3100
- xml = atob(svg);
3101
- } else if (svg.startsWith(utf8DataUri)) {
3102
- svg = svg.substring(utf8DataUri.length, svg.length);
3103
- xml = decodeURIComponent(svg);
3104
- } else {
3105
- xml = svg;
3106
- }
3107
- const doc = new DOMParser().parseFromString(xml, "text/xml");
3108
- const error = doc.querySelector("parsererror");
3109
- if (error) {
3110
- throw new Error(`${error.textContent ?? "parser error"}
3111
- ${xml}`);
3112
- }
3113
- return doc.documentElement;
3114
- } else {
3115
- return svg;
3116
- }
3117
- }
3118
- function parseSVG(svg) {
3119
- return parseNode(parseSVGToDOM(svg), {});
3120
- }
3121
-
3122
- function getPathsBoundingBox(paths, withStyle = true) {
3123
- if (!paths.length) {
3124
- return void 0;
3125
- }
3126
- const min = Vector2.MAX;
3127
- const max = Vector2.MIN;
3128
- paths.forEach((path) => path.getMinMax(min, max, withStyle));
3129
- return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
3130
- }
3131
- function pathsToSVGString(paths) {
3132
- const { x, y, width, height } = getPathsBoundingBox(paths);
3133
- const content = paths.map((path) => path.toSVGPathString()).join("");
3134
- return `<svg viewBox="${x} ${y} ${width} ${height}" width="${width}px" height="${height}px" xmlns="http://www.w3.org/2000/svg">${content}</svg>`;
3135
- }
3136
- function pathsToSVGUrl(paths) {
3137
- return `data:image/svg+xml;base64,${btoa(pathsToSVGString(paths))}`;
3138
- }
3139
- function pathsToSVG(paths) {
3140
- return new DOMParser().parseFromString(pathsToSVGString(paths), "image/svg+xml").documentElement;
3141
- }
3142
- function pathsToCanvas(paths, options = {}) {
3143
- const { pixelRatio = 2, ...style } = options;
3144
- const { left, top, width, height } = getPathsBoundingBox(paths);
3145
- const canvas = document.createElement("canvas");
3146
- canvas.width = width * pixelRatio;
3147
- canvas.height = height * pixelRatio;
3148
- canvas.style.width = `${width}px`;
3149
- canvas.style.height = `${height}px`;
3150
- const ctx = canvas.getContext("2d");
3151
- if (ctx) {
3152
- ctx.scale(pixelRatio, pixelRatio);
3153
- ctx.translate(-left, -top);
3154
- paths.forEach((path) => {
3155
- path.drawTo(ctx, style);
3156
- });
3157
- }
3158
- return canvas;
3899
+ function svgToPath2DSet(svg) {
3900
+ return new Path2DSet(parseNode(svgToDom(svg), {}));
3159
3901
  }
3160
3902
 
3903
+ exports.ArcCurve = ArcCurve;
3161
3904
  exports.BoundingBox = BoundingBox;
3162
- exports.CircleCurve = CircleCurve;
3905
+ exports.CompositeCurve = CompositeCurve;
3163
3906
  exports.CubicBezierCurve = CubicBezierCurve;
3164
3907
  exports.Curve = Curve;
3165
3908
  exports.CurvePath = CurvePath;
3166
3909
  exports.EllipseCurve = EllipseCurve;
3167
- exports.HeartCurve = HeartCurve;
3910
+ exports.EquilateralPloygonCurve = EquilateralPloygonCurve;
3168
3911
  exports.LineCurve = LineCurve;
3169
3912
  exports.Matrix3 = Matrix3;
3170
3913
  exports.Path2D = Path2D;
3914
+ exports.Path2DSet = Path2DSet;
3171
3915
  exports.PloygonCurve = PloygonCurve;
3172
3916
  exports.QuadraticBezierCurve = QuadraticBezierCurve;
3173
- exports.RectangularCurve = RectangularCurve;
3917
+ exports.RectangleCurve = RectangleCurve;
3918
+ exports.RoundRectangleCurve = RoundRectangleCurve;
3174
3919
  exports.SplineCurve = SplineCurve;
3175
3920
  exports.Vector2 = Vector2;
3176
- exports.addPathCommandsToPath2D = addPathCommandsToPath2D;
3177
- exports.getPathsBoundingBox = getPathsBoundingBox;
3921
+ exports.catmullRom = catmullRom;
3922
+ exports.cubicBezier = cubicBezier;
3923
+ exports.drawPoint = drawPoint;
3924
+ exports.fillTriangulate = fillTriangulate;
3925
+ exports.getAdaptiveCubicBezierCurvePoints = getAdaptiveCubicBezierCurvePoints;
3926
+ exports.getAdaptiveQuadraticBezierCurvePoints = getAdaptiveQuadraticBezierCurvePoints;
3178
3927
  exports.parseArcCommand = parseArcCommand;
3179
3928
  exports.parsePathDataArgs = parsePathDataArgs;
3180
- exports.parseSVG = parseSVG;
3181
- exports.parseSVGToDOM = parseSVGToDOM;
3182
- exports.pathCommandsToPathData = pathCommandsToPathData;
3183
- exports.pathDataToPathCommands = pathDataToPathCommands;
3184
- exports.pathsToCanvas = pathsToCanvas;
3185
- exports.pathsToSVG = pathsToSVG;
3186
- exports.pathsToSVGString = pathsToSVGString;
3187
- exports.pathsToSVGUrl = pathsToSVGUrl;
3929
+ exports.quadraticBezier = quadraticBezier;
3188
3930
  exports.setCanvasContext = setCanvasContext;
3931
+ exports.strokeTriangulate = strokeTriangulate;
3932
+ exports.svgPathCommandsAddToPath2D = svgPathCommandsAddToPath2D;
3933
+ exports.svgPathCommandsToData = svgPathCommandsToData;
3934
+ exports.svgPathDataToCommands = svgPathDataToCommands;
3935
+ exports.svgToDom = svgToDom;
3936
+ exports.svgToPath2DSet = svgToPath2DSet;