modern-path2d 1.0.1 → 1.1.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,684 @@ 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
+ getUnevenPointArray(count = 5, output = []) {
1599
+ const p = new Vector2();
1600
+ for (let i = 0, len = Math.max(1, count) - 1; i <= len; i++) {
1601
+ this.getPoint(i / len, p);
1602
+ output.push(p.x, p.y);
1603
+ }
1604
+ return output;
1605
+ }
1606
+ getSpacedPointArray(count = 5, output = []) {
1607
+ const p = new Vector2();
1608
+ for (let i = 0, len = Math.max(1, count) - 1; i <= len; i++) {
1609
+ this.getPointAt(i / len, p);
1610
+ output.push(p.x, p.y);
1012
1611
  }
1013
- return points;
1612
+ return output;
1014
1613
  }
1015
- forEachControlPoints(cb) {
1016
- this.getControlPoints().forEach(cb);
1017
- return this;
1614
+ getAdaptivePointArray(output = []) {
1615
+ return this.getUnevenPointArray(5, output);
1018
1616
  }
1019
- getSpacedPoints(divisions = 5) {
1020
- const points = [];
1021
- for (let i = 0; i <= divisions; i++) {
1022
- points.push(this.getPointAt(i / divisions));
1617
+ _pointArrayToPoint(array, output = []) {
1618
+ for (let i = 0, len = array.length; i < len; i += 2) {
1619
+ const x = array[i];
1620
+ const y = array[i + 1];
1621
+ output.push(new Vector2(x, y));
1622
+ }
1623
+ return output;
1624
+ }
1625
+ getSpacedPoints(count, output = []) {
1626
+ const array = this.getSpacedPointArray(count);
1627
+ this._pointArrayToPoint(array, output);
1628
+ return output;
1629
+ }
1630
+ getUnevenPoints(count, output = []) {
1631
+ const array = this.getUnevenPointArray(count);
1632
+ this._pointArrayToPoint(array, output);
1633
+ return output;
1634
+ }
1635
+ getAdaptivePoints(output = []) {
1636
+ const array = this.getAdaptivePointArray();
1637
+ this._pointArrayToPoint(array, output);
1638
+ return output;
1639
+ }
1640
+ getPoints(count, output = []) {
1641
+ let array;
1642
+ if (count) {
1643
+ array = this.getUnevenPointArray(count);
1644
+ } else {
1645
+ array = this.getAdaptivePointArray();
1023
1646
  }
1024
- return points;
1647
+ this._pointArrayToPoint(array, output);
1648
+ return output;
1025
1649
  }
1026
1650
  getLength() {
1027
1651
  const lengths = this.getLengths();
1028
1652
  return lengths[lengths.length - 1];
1029
1653
  }
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();
1654
+ getLengths() {
1655
+ if (!this._arcLengths || this._arcLengths.length !== this.arcLengthDivision + 1) {
1656
+ this.updateLengths();
1657
+ }
1658
+ return this._arcLengths;
1659
+ }
1660
+ updateLengths() {
1661
+ const divisions = this.arcLengthDivision;
1662
+ const arcLengths = [0];
1663
+ for (let sum = 0, prev = this.getPoint(0), i = 1; i <= divisions; i++) {
1664
+ const current = this.getPoint(i / divisions);
1665
+ sum += current.distanceTo(prev);
1666
+ arcLengths.push(sum);
1667
+ prev = current;
1668
+ }
1669
+ this._arcLengths = arcLengths;
1052
1670
  }
1053
1671
  getUToTMapping(u, distance) {
1054
1672
  const lengths = this.getLengths();
1055
- let i = 0;
1056
1673
  const lengthsLen = lengths.length;
1057
- let targetLength;
1058
- if (distance) {
1059
- targetLength = distance;
1060
- } else {
1061
- targetLength = u * lengths[lengthsLen - 1];
1674
+ const targetLen = distance ?? u * lengths[lengthsLen - 1];
1675
+ if (lengthsLen < 2) {
1676
+ return targetLen / lengths[0];
1062
1677
  }
1678
+ let i = 0;
1063
1679
  let low = 0;
1064
1680
  let high = lengthsLen - 1;
1065
1681
  let comparison;
1066
1682
  while (low <= high) {
1067
1683
  i = Math.floor(low + (high - low) / 2);
1068
- comparison = lengths[i] - targetLength;
1684
+ comparison = lengths[i] - targetLen;
1069
1685
  if (comparison < 0) {
1070
1686
  low = i + 1;
1071
1687
  } else if (comparison > 0) {
@@ -1076,13 +1692,13 @@ class Curve {
1076
1692
  }
1077
1693
  }
1078
1694
  i = high;
1079
- if (lengths[i] === targetLength) {
1695
+ if (lengths[i] === targetLen) {
1080
1696
  return i / (lengthsLen - 1);
1081
1697
  }
1082
- const lengthBefore = lengths[i];
1083
- const lengthAfter = lengths[i + 1];
1084
- const segmentLength = lengthAfter - lengthBefore;
1085
- const segmentFraction = (targetLength - lengthBefore) / segmentLength;
1698
+ const before = lengths[i];
1699
+ const after = lengths[i + 1];
1700
+ const segmentLength = after - before;
1701
+ const segmentFraction = (targetLen - before) / segmentLength;
1086
1702
  return (i + segmentFraction) / (lengthsLen - 1);
1087
1703
  }
1088
1704
  getTangent(t, output = new Vector2()) {
@@ -1121,31 +1737,55 @@ class Curve {
1121
1737
  return mid;
1122
1738
  }
1123
1739
  matrix(matrix) {
1124
- this.forEachControlPoints((point) => point.applyMatrix3(matrix));
1740
+ this.getControlPointRefs().forEach((point) => point.applyMatrix3(matrix));
1125
1741
  return this;
1126
1742
  }
1127
1743
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1128
- this.getPoints().forEach((point) => {
1129
- min.min(point);
1130
- max.max(point);
1131
- });
1744
+ const potins = this.getPoints();
1745
+ for (let i = 0, len = potins.length; i < len; i++) {
1746
+ const p = potins[i];
1747
+ min.min(p);
1748
+ max.max(p);
1749
+ }
1132
1750
  return { min, max };
1133
1751
  }
1134
1752
  getBoundingBox() {
1135
1753
  const { min, max } = this.getMinMax();
1136
1754
  return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
1137
1755
  }
1756
+ fillTriangulate(options) {
1757
+ return fillTriangulate(
1758
+ this.getPoints().reduce((arr, p) => {
1759
+ arr.push(p.x, p.y);
1760
+ return arr;
1761
+ }, []),
1762
+ options
1763
+ );
1764
+ }
1765
+ strokeTriangulate(options) {
1766
+ return strokeTriangulate(
1767
+ this.getPoints().reduce((arr, p) => {
1768
+ arr.push(p.x, p.y);
1769
+ return arr;
1770
+ }, []),
1771
+ options
1772
+ );
1773
+ }
1138
1774
  toCommands() {
1139
- return this.getPoints().map((point, i) => {
1775
+ const comds = [];
1776
+ const potins = this.getPoints();
1777
+ for (let i = 0, len = potins.length; i < len; i++) {
1778
+ const p = potins[i];
1140
1779
  if (i === 0) {
1141
- return { type: "M", x: point.x, y: point.y };
1780
+ comds.push({ type: "M", x: p.x, y: p.y });
1142
1781
  } else {
1143
- return { type: "L", x: point.x, y: point.y };
1782
+ comds.push({ type: "L", x: p.x, y: p.y });
1144
1783
  }
1145
- });
1784
+ }
1785
+ return comds;
1146
1786
  }
1147
1787
  toData() {
1148
- return pathCommandsToPathData(this.toCommands());
1788
+ return svgPathCommandsToData(this.toCommands());
1149
1789
  }
1150
1790
  drawTo(ctx) {
1151
1791
  this.toCommands().forEach((cmd) => {
@@ -1161,7 +1801,7 @@ class Curve {
1161
1801
  return this;
1162
1802
  }
1163
1803
  copy(source) {
1164
- this.arcLengthDivisions = source.arcLengthDivisions;
1804
+ this.arcLengthDivision = source.arcLengthDivision;
1165
1805
  return this;
1166
1806
  }
1167
1807
  clone() {
@@ -1169,179 +1809,22 @@ class Curve {
1169
1809
  }
1170
1810
  }
1171
1811
 
1172
- class CircleCurve extends Curve {
1173
- constructor(center, radius, start = 0, end = Math.PI * 2) {
1174
- 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));
1183
- }
1184
- getTangent(t, output = new Vector2()) {
1185
- const { x, y } = this.getNormal(t);
1186
- return output.set(-y, x);
1187
- }
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));
1192
- }
1193
- getControlPoints() {
1194
- return [this.center];
1195
- }
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;
1250
- }
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
- );
1257
- }
1258
- getControlPoints() {
1259
- return [
1260
- this.start,
1261
- this.startControl,
1262
- this.endControl,
1263
- this.end
1264
- ];
1265
- }
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);
1274
- }
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 };
1306
- }
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
- ];
1313
- }
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;
1319
- }
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;
1327
- }
1328
- }
1329
-
1330
1812
  const tempTransform0$1 = new Matrix3();
1331
1813
  const tempTransform1$1 = new Matrix3();
1332
1814
  const tempTransform2$1 = new Matrix3();
1333
1815
  const tempV2 = new Vector2();
1334
1816
  class EllipseCurve extends Curve {
1335
- constructor(center = new Vector2(), radiusX = 1, radiusY = 1, rotation = 0, startAngle = 0, endAngle = Math.PI * 2, clockwise = false) {
1817
+ constructor(cx = 0, cy = 0, rx = 1, ry = 1, rotation = 0, startAngle = 0, endAngle = Math.PI * 2, clockwise = false) {
1336
1818
  super();
1337
- this.center = center;
1338
- this.radiusX = radiusX;
1339
- this.radiusY = radiusY;
1340
1819
  this.rotation = rotation;
1341
1820
  this.startAngle = startAngle;
1342
1821
  this.endAngle = endAngle;
1343
1822
  this.clockwise = clockwise;
1823
+ this.center = new Vector2(cx, cy);
1824
+ this.radius = new Vector2(rx, ry);
1344
1825
  }
1826
+ center;
1827
+ radius;
1345
1828
  isClockwise() {
1346
1829
  return this.clockwise;
1347
1830
  }
@@ -1366,8 +1849,8 @@ class EllipseCurve extends Curve {
1366
1849
  }
1367
1850
  }
1368
1851
  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);
1852
+ let _x = this.center.x + this.radius.x * Math.cos(angle);
1853
+ let _y = this.center.y + this.radius.y * Math.sin(angle);
1371
1854
  if (this.rotation !== 0) {
1372
1855
  const cos = Math.cos(this.rotation);
1373
1856
  const sin = Math.sin(this.rotation);
@@ -1379,8 +1862,9 @@ class EllipseCurve extends Curve {
1379
1862
  return output.set(_x, _y);
1380
1863
  }
1381
1864
  toCommands() {
1382
- const { center, radiusX: rx, radiusY: ry, startAngle, endAngle, clockwise, rotation } = this;
1865
+ const { center, radius, startAngle, endAngle, clockwise, rotation } = this;
1383
1866
  const { x: cx, y: cy } = center;
1867
+ const { x: rx, y: ry } = radius;
1384
1868
  const startX = cx + rx * Math.cos(startAngle) * Math.cos(rotation) - ry * Math.sin(startAngle) * Math.sin(rotation);
1385
1869
  const startY = cy + rx * Math.cos(startAngle) * Math.sin(rotation) + ry * Math.sin(startAngle) * Math.cos(rotation);
1386
1870
  const angleDiff = Math.abs(startAngle - endAngle);
@@ -1406,12 +1890,12 @@ class EllipseCurve extends Curve {
1406
1890
  }
1407
1891
  }
1408
1892
  drawTo(ctx) {
1409
- const { center, radiusX, radiusY, rotation, startAngle, endAngle, clockwise } = this;
1893
+ const { center, radius, rotation, startAngle, endAngle, clockwise } = this;
1410
1894
  ctx.ellipse(
1411
1895
  center.x,
1412
1896
  center.y,
1413
- radiusX,
1414
- radiusY,
1897
+ radius.x,
1898
+ radius.y,
1415
1899
  rotation,
1416
1900
  startAngle,
1417
1901
  endAngle,
@@ -1431,11 +1915,119 @@ class EllipseCurve extends Curve {
1431
1915
  }
1432
1916
  return this;
1433
1917
  }
1434
- getControlPoints() {
1918
+ getControlPointRefs() {
1435
1919
  return [this.center];
1436
1920
  }
1921
+ getAdaptivePointArray(output = []) {
1922
+ const i = output.length - 1;
1923
+ const x = this.center.x;
1924
+ const y = this.center.y;
1925
+ const rx = this.radius.x;
1926
+ const ry = this.radius.y;
1927
+ const dx = 0;
1928
+ const dy = 0;
1929
+ if (!(rx >= 0 && ry >= 0 && dx >= 0 && dy >= 0)) {
1930
+ return output;
1931
+ }
1932
+ const n = Math.ceil(2.3 * Math.sqrt(rx + ry));
1933
+ const m = n * 8 + (0) + (0);
1934
+ if (m === 0) {
1935
+ return output;
1936
+ }
1937
+ if (n === 0) {
1938
+ output[i] = output[i + 6] = x + dx;
1939
+ output[i + 1] = output[i + 3] = y + dy;
1940
+ output[i + 2] = output[i + 4] = x - dx;
1941
+ output[i + 5] = output[i + 7] = y - dy;
1942
+ return output;
1943
+ }
1944
+ let j1 = i;
1945
+ let j2 = n * 4 + (0) + 2 + i;
1946
+ let j3 = j2;
1947
+ let j4 = m + i;
1948
+ let x0 = dx + rx;
1949
+ let y0 = dy;
1950
+ let x1 = x + x0;
1951
+ let x2 = x - x0;
1952
+ let y1 = y + y0;
1953
+ output[j1++] = x1;
1954
+ output[j1++] = y1;
1955
+ output[--j2] = y1;
1956
+ output[--j2] = x2;
1957
+ for (let i2 = 1; i2 < n; i2++) {
1958
+ const a = Math.PI / 2 * (i2 / n);
1959
+ const x02 = dx + Math.cos(a) * rx;
1960
+ const y02 = dy + Math.sin(a) * ry;
1961
+ const x12 = x + x02;
1962
+ const x22 = x - x02;
1963
+ const y12 = y + y02;
1964
+ const y22 = y - y02;
1965
+ output[j1++] = x12;
1966
+ output[j1++] = y12;
1967
+ output[--j2] = y12;
1968
+ output[--j2] = x22;
1969
+ output[j3++] = x22;
1970
+ output[j3++] = y22;
1971
+ output[--j4] = y22;
1972
+ output[--j4] = x12;
1973
+ }
1974
+ x0 = dx;
1975
+ y0 = dy + ry;
1976
+ x1 = x + x0;
1977
+ x2 = x - x0;
1978
+ y1 = y + y0;
1979
+ const y2 = y - y0;
1980
+ output[j1++] = x1;
1981
+ output[j1++] = y1;
1982
+ output[--j4] = y2;
1983
+ output[--j4] = x1;
1984
+ return output;
1985
+ }
1986
+ fillTriangulate(options = {}) {
1987
+ let {
1988
+ vertices = [],
1989
+ indices = [],
1990
+ verticesStride = 2,
1991
+ verticesOffset = 0,
1992
+ indicesOffset = 0
1993
+ } = options;
1994
+ const points = this.getAdaptivePointArray();
1995
+ if (points.length === 0) {
1996
+ return { vertices, indices };
1997
+ }
1998
+ let centerX = 0;
1999
+ let centerY = 0;
2000
+ for (let i = 0; i < points.length; i += 2) {
2001
+ centerX += points[i];
2002
+ centerY += points[i + 1];
2003
+ }
2004
+ centerX /= points.length / 2;
2005
+ centerY /= points.length / 2;
2006
+ let count = verticesOffset;
2007
+ vertices[count * verticesStride] = centerX;
2008
+ vertices[count * verticesStride + 1] = centerY;
2009
+ const centerIndex = count++;
2010
+ for (let i = 0; i < points.length; i += 2) {
2011
+ vertices[count * verticesStride] = points[i];
2012
+ vertices[count * verticesStride + 1] = points[i + 1];
2013
+ if (i > 0) {
2014
+ indices[indicesOffset++] = count;
2015
+ indices[indicesOffset++] = centerIndex;
2016
+ indices[indicesOffset++] = count - 1;
2017
+ }
2018
+ count++;
2019
+ }
2020
+ indices[indicesOffset++] = centerIndex + 1;
2021
+ indices[indicesOffset++] = centerIndex;
2022
+ indices[indicesOffset++] = count - 1;
2023
+ return {
2024
+ vertices,
2025
+ indices
2026
+ };
2027
+ }
1437
2028
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1438
- const { center, radiusX: rx, radiusY: ry, rotation: theta } = this;
2029
+ const { center, radius, rotation: theta } = this;
2030
+ const { x: rx, y: ry } = radius;
1439
2031
  const { x: cx, y: cy } = center;
1440
2032
  const cosTheta = Math.cos(theta);
1441
2033
  const sinTheta = Math.sin(theta);
@@ -1455,8 +2047,8 @@ class EllipseCurve extends Curve {
1455
2047
  super.copy(source);
1456
2048
  this.center.x = source.center.x;
1457
2049
  this.center.y = source.center.y;
1458
- this.radiusX = source.radiusX;
1459
- this.radiusY = source.radiusY;
2050
+ this.radius.x = source.radius.x;
2051
+ this.radius.y = source.radius.y;
1460
2052
  this.startAngle = source.startAngle;
1461
2053
  this.endAngle = source.endAngle;
1462
2054
  this.clockwise = source.clockwise;
@@ -1465,8 +2057,8 @@ class EllipseCurve extends Curve {
1465
2057
  }
1466
2058
  }
1467
2059
  function transfEllipseGeneric(curve, m) {
1468
- const a = curve.radiusX;
1469
- const b = curve.radiusY;
2060
+ const a = curve.radius.x;
2061
+ const b = curve.radius.y;
1470
2062
  const cosTheta = Math.cos(curve.rotation);
1471
2063
  const sinTheta = Math.sin(curve.rotation);
1472
2064
  const v1 = new Vector2(a * cosTheta, a * sinTheta);
@@ -1491,8 +2083,8 @@ function transfEllipseGeneric(curve, m) {
1491
2083
  const ed = eigenDecomposition(mQe[0], mQe[1], mQe[4]);
1492
2084
  const rt1sqrt = Math.sqrt(ed.rt1);
1493
2085
  const rt2sqrt = Math.sqrt(ed.rt2);
1494
- curve.radiusX = 1 / rt1sqrt;
1495
- curve.radiusY = 1 / rt2sqrt;
2086
+ curve.radius.x = 1 / rt1sqrt;
2087
+ curve.radius.y = 1 / rt2sqrt;
1496
2088
  curve.rotation = Math.atan2(ed.sn, ed.cs);
1497
2089
  const isFullEllipse = (curve.endAngle - curve.startAngle) % (2 * Math.PI) < Number.EPSILON;
1498
2090
  if (!isFullEllipse) {
@@ -1533,8 +2125,8 @@ function transfEllipseGeneric(curve, m) {
1533
2125
  function transfEllipseNoSkew(curve, m) {
1534
2126
  const sx = getTransformScaleX(m);
1535
2127
  const sy = getTransformScaleY(m);
1536
- curve.radiusX *= sx;
1537
- curve.radiusY *= sy;
2128
+ curve.radius.x *= sx;
2129
+ curve.radius.y *= sy;
1538
2130
  const theta = sx > Number.EPSILON ? Math.atan2(m.elements[1], m.elements[0]) : Math.atan2(-m.elements[3], m.elements[4]);
1539
2131
  curve.rotation += theta;
1540
2132
  if (isTransformFlipped(m)) {
@@ -1596,357 +2188,455 @@ function eigenDecomposition(A, B, C) {
1596
2188
  cs = 1 / Math.sqrt(1 + t * t);
1597
2189
  sn = t * cs;
1598
2190
  }
1599
- if (df > 0) {
1600
- t = cs;
1601
- cs = -sn;
1602
- sn = t;
2191
+ if (df > 0) {
2192
+ t = cs;
2193
+ cs = -sn;
2194
+ sn = t;
2195
+ }
2196
+ return { rt1, rt2, cs, sn };
2197
+ }
2198
+
2199
+ class ArcCurve extends EllipseCurve {
2200
+ constructor(cx = 0, cy = 0, radius = 1, startAngle = 0, endAngle = Math.PI * 2, clockwise = false) {
2201
+ super(cx, cy, radius, radius, 0, startAngle, endAngle, clockwise);
2202
+ }
2203
+ getAdaptivePointArray(output = []) {
2204
+ const start = 0;
2205
+ const end = 1;
2206
+ const x = this.center.x;
2207
+ const y = this.center.y;
2208
+ const clockwise = this.clockwise;
2209
+ const radius = this.radius.x;
2210
+ let dist = Math.abs(start - end);
2211
+ if (clockwise && end > start) {
2212
+ dist = 2 * Math.PI - dist;
2213
+ }
2214
+ let steps = Math.max(6, Math.floor(6 * radius ** (1 / 3) * (dist / Math.PI)));
2215
+ steps = Math.max(steps, 3);
2216
+ let f = dist / steps;
2217
+ let t = start;
2218
+ f *= clockwise ? -1 : 1;
2219
+ for (let i = 0; i < steps + 1; i++) {
2220
+ const cs = Math.cos(t);
2221
+ const sn = Math.sin(t);
2222
+ const nx = x + cs * radius;
2223
+ const ny = y + sn * radius;
2224
+ output.push(nx, ny);
2225
+ t += f;
2226
+ }
2227
+ return output;
2228
+ }
2229
+ }
2230
+
2231
+ class CompositeCurve extends Curve {
2232
+ constructor(curves = []) {
2233
+ super();
2234
+ this.curves = curves;
2235
+ }
2236
+ addCurve(curve) {
2237
+ this.curves.push(curve);
2238
+ return this;
2239
+ }
2240
+ getPoint(t, output = new Vector2()) {
2241
+ const d = t * this.getLength();
2242
+ const lengths = this.getLengths();
2243
+ let i = 0;
2244
+ while (i < lengths.length) {
2245
+ if (lengths[i] >= d) {
2246
+ const diff = lengths[i] - d;
2247
+ const curve = this.curves[i];
2248
+ const length = curve.getLength();
2249
+ return curve.getPointAt(
2250
+ length === 0 ? 0 : 1 - diff / length,
2251
+ output
2252
+ );
2253
+ }
2254
+ i++;
2255
+ }
2256
+ return output;
2257
+ }
2258
+ updateLengths() {
2259
+ const arcLengths = [];
2260
+ for (let i = 0, sum = 0, len = this.curves.length; i < len; i++) {
2261
+ sum += this.curves[i].getLength();
2262
+ arcLengths.push(sum);
2263
+ }
2264
+ this._arcLengths = arcLengths;
2265
+ }
2266
+ getControlPointRefs() {
2267
+ return this.curves.flatMap((curve) => curve.getControlPointRefs());
2268
+ }
2269
+ getAdaptivePointArray(output = []) {
2270
+ let offset;
2271
+ this.curves.forEach((curve) => {
2272
+ curve.getAdaptivePointArray(output);
2273
+ if (offset) {
2274
+ if (output[offset - 1] === output[offset + 1] && output[offset] === output[offset + 2]) {
2275
+ output.splice(offset + 1, 2);
2276
+ }
2277
+ }
2278
+ offset = output.length - 1;
2279
+ });
2280
+ return output;
2281
+ }
2282
+ getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
2283
+ this.curves.forEach((curve) => curve.getMinMax(min, max));
2284
+ return { min, max };
2285
+ }
2286
+ getBoundingBox() {
2287
+ const { min, max } = this.getMinMax();
2288
+ return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
2289
+ }
2290
+ toCommands() {
2291
+ return this.curves.flatMap((curve) => curve.toCommands());
2292
+ }
2293
+ drawTo(ctx) {
2294
+ const point = this.curves[0]?.getPoint(0);
2295
+ if (point) {
2296
+ ctx.moveTo(point.x, point.y);
2297
+ }
2298
+ this.curves.forEach((curve) => curve.drawTo(ctx));
2299
+ return this;
1603
2300
  }
1604
- return { rt1, rt2, cs, sn };
1605
2301
  }
1606
2302
 
1607
- class LineCurve extends Curve {
1608
- constructor(start = new Vector2(), end = new Vector2()) {
2303
+ class CubicBezierCurve extends Curve {
2304
+ p1;
2305
+ cp1;
2306
+ cp2;
2307
+ p2;
2308
+ static from(p1, cp1, cp2, p2) {
2309
+ return new CubicBezierCurve(
2310
+ p1.x,
2311
+ p1.y,
2312
+ cp1.x,
2313
+ cp1.y,
2314
+ cp2.x,
2315
+ cp2.y,
2316
+ p2.x,
2317
+ p2.y
2318
+ );
2319
+ }
2320
+ constructor(p1x, p1y, cp1x, cp1y, cp2x, cp2y, p2x, p2y) {
1609
2321
  super();
1610
- this.start = start;
1611
- this.end = end;
2322
+ this.p1 = new Vector2(p1x, p1y);
2323
+ this.cp1 = new Vector2(cp1x, cp1y);
2324
+ this.cp2 = new Vector2(cp2x, cp2y);
2325
+ this.p2 = new Vector2(p2x, p2y);
1612
2326
  }
1613
2327
  getPoint(t, output = new Vector2()) {
1614
- if (t === 1) {
1615
- output.copy(this.end);
1616
- } else {
1617
- output.copy(this.end).sub(this.start).scale(t).add(this.start);
1618
- }
1619
- return output;
1620
- }
1621
- getPointAt(u, output = new Vector2()) {
1622
- return this.getPoint(u, output);
2328
+ const { p1, cp1, cp2, p2 } = this;
2329
+ return output.set(
2330
+ cubicBezier(t, p1.x, cp1.x, cp2.x, p2.x),
2331
+ cubicBezier(t, p1.y, cp1.y, cp2.y, p2.y)
2332
+ );
1623
2333
  }
1624
- getTangent(_t, output = new Vector2()) {
1625
- return output.subVectors(this.end, this.start).normalize();
2334
+ getAdaptivePointArray(output = []) {
2335
+ return getAdaptiveCubicBezierCurvePoints(
2336
+ this.p1.x,
2337
+ this.p1.y,
2338
+ this.cp1.x,
2339
+ this.cp1.y,
2340
+ this.cp2.x,
2341
+ this.cp2.y,
2342
+ this.p2.x,
2343
+ this.p2.y,
2344
+ 0.5,
2345
+ output
2346
+ );
1626
2347
  }
1627
- getTangentAt(u, output = new Vector2()) {
1628
- return this.getTangent(u, output);
2348
+ getControlPointRefs() {
2349
+ return [this.p1, this.cp1, this.cp2, this.p2];
1629
2350
  }
1630
- getControlPoints() {
1631
- return [
1632
- this.start,
1633
- this.end
1634
- ];
2351
+ _solveQuadratic(a, b, c) {
2352
+ const discriminant = b * b - 4 * a * c;
2353
+ if (discriminant < 0)
2354
+ return [];
2355
+ const sqrtDiscriminant = Math.sqrt(discriminant);
2356
+ const t1 = (-b + sqrtDiscriminant) / (2 * a);
2357
+ const t2 = (-b - sqrtDiscriminant) / (2 * a);
2358
+ return [t1, t2].filter((t) => t >= 0 && t <= 1);
1635
2359
  }
1636
2360
  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);
2361
+ const { p1, cp1, cp2, p2 } = this;
2362
+ const dxRoots = this._solveQuadratic(
2363
+ 3 * (cp1.x - p1.x),
2364
+ 6 * (cp2.x - cp1.x),
2365
+ 3 * (p2.x - cp2.x)
2366
+ );
2367
+ const dyRoots = this._solveQuadratic(
2368
+ 3 * (cp1.y - p1.y),
2369
+ 6 * (cp2.y - cp1.y),
2370
+ 3 * (p2.y - cp2.y)
2371
+ );
2372
+ const tValues = [0, 1, ...dxRoots, ...dyRoots];
2373
+ const samplePoints = (tValues2, precision) => {
2374
+ for (const t of tValues2) {
2375
+ for (let i = 0; i <= precision; i++) {
2376
+ const delta = i / precision - 0.5;
2377
+ const refinedT = Math.min(1, Math.max(0, t + delta));
2378
+ const point = this.getPoint(refinedT);
2379
+ min.x = Math.min(min.x, point.x);
2380
+ min.y = Math.min(min.y, point.y);
2381
+ max.x = Math.max(max.x, point.x);
2382
+ max.y = Math.max(max.y, point.y);
2383
+ }
2384
+ }
2385
+ };
2386
+ samplePoints(tValues, 10);
1642
2387
  return { min, max };
1643
2388
  }
1644
2389
  toCommands() {
1645
- const { start, end } = this;
2390
+ const { p1, cp1, cp2, p2 } = this;
1646
2391
  return [
1647
- { type: "M", x: start.x, y: start.y },
1648
- { type: "L", x: end.x, y: end.y }
2392
+ { type: "M", x: p1.x, y: p1.y },
2393
+ { type: "C", x1: cp1.x, y1: cp1.y, x2: cp2.x, y2: cp2.y, x: p2.x, y: p2.y }
1649
2394
  ];
1650
2395
  }
1651
2396
  drawTo(ctx) {
1652
- const { start, end } = this;
1653
- ctx.lineTo(start.x, start.y);
1654
- ctx.lineTo(end.x, end.y);
2397
+ const { p1, cp1, cp2, p2 } = this;
2398
+ ctx.lineTo(p1.x, p1.y);
2399
+ ctx.bezierCurveTo(cp1.x, cp1.y, cp2.x, cp2.y, p2.x, p2.y);
1655
2400
  return this;
1656
2401
  }
1657
2402
  copy(source) {
1658
2403
  super.copy(source);
1659
- this.start.copy(source.start);
1660
- this.end.copy(source.end);
2404
+ this.p1.copy(source.p1);
2405
+ this.cp1.copy(source.cp1);
2406
+ this.cp2.copy(source.cp2);
2407
+ this.p2.copy(source.p2);
1661
2408
  return this;
1662
2409
  }
1663
2410
  }
1664
2411
 
1665
- class HeartCurve extends Curve {
1666
- constructor(center, size, start = 0, end = 1) {
2412
+ class LineCurve extends Curve {
2413
+ p1;
2414
+ p2;
2415
+ static from(p1, p2) {
2416
+ return new LineCurve(p1.x, p1.y, p2.x, p2.y);
2417
+ }
2418
+ constructor(p1x, p1y, p2x, p2y) {
1667
2419
  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;
2420
+ this.p1 = new Vector2(p1x, p1y);
2421
+ this.p2 = new Vector2(p2x, p2y);
2422
+ }
2423
+ getPoint(t, output = new Vector2()) {
2424
+ if (t === 1) {
2425
+ output.copy(this.p2);
1717
2426
  } else {
1718
- index = 4;
1719
- this.curveT = (val - 5 * Math.PI / 8 - 1.5) / PI_1_2;
2427
+ output.copy(this.p2).sub(this.p1).scale(t).add(this.p1);
1720
2428
  }
1721
- return this.curves[index];
2429
+ return output;
2430
+ }
2431
+ getPointAt(u, output = new Vector2()) {
2432
+ return this.getPoint(u, output);
2433
+ }
2434
+ getTangent(_t, output = new Vector2()) {
2435
+ return output.subVectors(this.p2, this.p1).normalize();
1722
2436
  }
1723
- getTangent(t, output) {
1724
- return this.getCurve(t).getTangent(this.curveT, output);
2437
+ getTangentAt(u, output = new Vector2()) {
2438
+ return this.getTangent(u, output);
1725
2439
  }
1726
- getNormal(t, output) {
1727
- return this.getCurve(t).getNormal(this.curveT, output);
2440
+ getControlPointRefs() {
2441
+ return [this.p1, this.p2];
1728
2442
  }
1729
- getControlPoints() {
1730
- return this.curves.flatMap((curve) => curve.getControlPoints());
2443
+ getAdaptivePointArray(output = []) {
2444
+ output.push(
2445
+ this.p1.x,
2446
+ this.p1.y,
2447
+ this.p2.x,
2448
+ this.p2.y
2449
+ );
2450
+ return output;
1731
2451
  }
1732
2452
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1733
- this.curves.forEach((curve) => curve.getMinMax(min, max));
2453
+ const { p1, p2 } = this;
2454
+ min.x = Math.min(min.x, p1.x, p2.x);
2455
+ min.y = Math.min(min.y, p1.y, p2.y);
2456
+ max.x = Math.max(max.x, p1.x, p2.x);
2457
+ max.y = Math.max(max.y, p1.y, p2.y);
1734
2458
  return { min, max };
1735
2459
  }
1736
2460
  toCommands() {
1737
- return this.curves.flatMap((curve) => curve.toCommands());
2461
+ const { p1, p2 } = this;
2462
+ return [
2463
+ { type: "M", x: p1.x, y: p1.y },
2464
+ { type: "L", x: p2.x, y: p2.y }
2465
+ ];
1738
2466
  }
1739
2467
  drawTo(ctx) {
1740
- this.curves.forEach((curve) => curve.drawTo(ctx));
2468
+ const { p1, p2 } = this;
2469
+ ctx.lineTo(p1.x, p1.y);
2470
+ ctx.lineTo(p2.x, p2.y);
2471
+ return this;
2472
+ }
2473
+ copy(source) {
2474
+ super.copy(source);
2475
+ this.p1.copy(source.p1);
2476
+ this.p2.copy(source.p2);
1741
2477
  return this;
1742
2478
  }
1743
2479
  }
1744
2480
 
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(
2481
+ class PloygonCurve extends CompositeCurve {
2482
+ static equilateral(cx = 0, cy = 0, radius = 1, sideCount = 3) {
2483
+ const ploygon = new PloygonCurve();
2484
+ const points = [];
2485
+ for (let i = 0; i < sideCount; i++) {
2486
+ const radian = i * 2 * Math.PI / sideCount - 0.5 * Math.PI;
2487
+ points.push(
1763
2488
  new Vector2(
1764
- this.radius * Math.cos(radian),
1765
- this.radius * Math.sin(radian)
1766
- ).add(this.center)
2489
+ radius * Math.cos(radian),
2490
+ radius * Math.sin(radian)
2491
+ ).add({ x: cx, y: cy })
1767
2492
  );
1768
2493
  }
1769
- for (let i = 0; i < this.number; i++) {
1770
- this.curves.push(new LineCurve(this.points[i], this.points[(i + 1) % this.number]));
2494
+ for (let i = 0; i < sideCount; i++) {
2495
+ ploygon.curves.push(
2496
+ LineCurve.from(points[i], points[(i + 1) % sideCount])
2497
+ );
1771
2498
  }
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;
2499
+ return ploygon;
1807
2500
  }
1808
2501
  }
1809
2502
 
1810
2503
  class QuadraticBezierCurve extends Curve {
1811
- constructor(start = new Vector2(), control = new Vector2(), end = new Vector2()) {
2504
+ p1;
2505
+ cp;
2506
+ p2;
2507
+ static from(p1, cp, p2) {
2508
+ return new QuadraticBezierCurve(
2509
+ p1.x,
2510
+ p1.y,
2511
+ cp.x,
2512
+ cp.y,
2513
+ p2.x,
2514
+ p2.y
2515
+ );
2516
+ }
2517
+ constructor(p1x, p1y, cpx, cpy, p2x, p2y) {
1812
2518
  super();
1813
- this.start = start;
1814
- this.control = control;
1815
- this.end = end;
2519
+ this.p1 = new Vector2(p1x, p1y);
2520
+ this.cp = new Vector2(cpx, cpy);
2521
+ this.p2 = new Vector2(p2x, p2y);
1816
2522
  }
1817
2523
  getPoint(t, output = new Vector2()) {
1818
- const { start, control, end } = this;
2524
+ const { p1, cp, p2 } = this;
1819
2525
  output.set(
1820
- quadraticBezier(t, start.x, control.x, end.x),
1821
- quadraticBezier(t, start.y, control.y, end.y)
2526
+ quadraticBezier(t, p1.x, cp.x, p2.x),
2527
+ quadraticBezier(t, p1.y, cp.y, p2.y)
1822
2528
  );
1823
2529
  return output;
1824
2530
  }
1825
- getControlPoints() {
1826
- return [
1827
- this.start,
1828
- this.control,
1829
- this.end
1830
- ];
2531
+ getControlPointRefs() {
2532
+ return [this.p1, this.cp, this.p2];
2533
+ }
2534
+ getAdaptivePointArray(output = []) {
2535
+ return getAdaptiveQuadraticBezierCurvePoints(
2536
+ this.p1.x,
2537
+ this.p1.y,
2538
+ this.cp.x,
2539
+ this.cp.y,
2540
+ this.p2.x,
2541
+ this.p2.y,
2542
+ 0.5,
2543
+ output
2544
+ );
1831
2545
  }
1832
2546
  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);
2547
+ const { p1, cp, p2 } = this;
2548
+ const x1 = 0.5 * (p1.x + cp.x);
2549
+ const y1 = 0.5 * (p1.y + cp.y);
2550
+ const x2 = 0.5 * (p1.x + p2.x);
2551
+ const y2 = 0.5 * (p1.y + p2.y);
2552
+ min.x = Math.min(min.x, p1.x, p2.x, x1, x2);
2553
+ min.y = Math.min(min.y, p1.y, p2.y, y1, y2);
2554
+ max.x = Math.max(max.x, p1.x, p2.x, x1, x2);
2555
+ max.y = Math.max(max.y, p1.y, p2.y, y1, y2);
1842
2556
  return { min, max };
1843
2557
  }
1844
2558
  toCommands() {
1845
- const { start, control, end } = this;
2559
+ const { p1, cp, p2 } = this;
1846
2560
  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 }
2561
+ { type: "M", x: p1.x, y: p1.y },
2562
+ { type: "Q", x1: cp.x, y1: cp.y, x: p2.x, y: p2.y }
1849
2563
  ];
1850
2564
  }
1851
2565
  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);
2566
+ const { p1, cp, p2 } = this;
2567
+ ctx.lineTo(p1.x, p1.y);
2568
+ ctx.quadraticCurveTo(cp.x, cp.y, p2.x, p2.y);
1855
2569
  return this;
1856
2570
  }
1857
2571
  copy(source) {
1858
2572
  super.copy(source);
1859
- this.start.copy(source.start);
1860
- this.control.copy(source.control);
1861
- this.end.copy(source.end);
2573
+ this.p1.copy(source.p1);
2574
+ this.cp.copy(source.cp);
2575
+ this.p2.copy(source.p2);
1862
2576
  return this;
1863
2577
  }
1864
2578
  }
1865
2579
 
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;
2580
+ class RectangleCurve extends PloygonCurve {
2581
+ constructor(x = 0, y = 0, width = 0, height = 0) {
1894
2582
  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)
2583
+ new Vector2(x, y),
2584
+ new Vector2(x + width, y),
2585
+ new Vector2(x + width, y + height),
2586
+ new Vector2(x, y + height)
1899
2587
  ];
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());
1939
- }
1940
- getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1941
- this.curves.forEach((curve) => curve.getMinMax(min, max));
1942
- return { min, max };
1943
- }
1944
- toCommands() {
1945
- return this.curves.flatMap((curve) => curve.toCommands());
2588
+ super([
2589
+ LineCurve.from(points[0], points[1]),
2590
+ LineCurve.from(points[1], points[2]),
2591
+ LineCurve.from(points[2], points[3]),
2592
+ LineCurve.from(points[3], points[0])
2593
+ ]);
2594
+ this.x = x;
2595
+ this.y = y;
2596
+ this.width = width;
2597
+ this.height = height;
1946
2598
  }
1947
- drawTo(ctx) {
1948
- this.curves.forEach((curve) => curve.drawTo(ctx));
1949
- return this;
2599
+ fillTriangulate(options = {}) {
2600
+ let {
2601
+ vertices = [],
2602
+ indices = [],
2603
+ verticesStride = 2,
2604
+ verticesOffset = 0,
2605
+ indicesOffset = 0
2606
+ } = options;
2607
+ const { x, y, width, height } = this;
2608
+ const points = [
2609
+ x,
2610
+ y,
2611
+ x + width,
2612
+ y,
2613
+ x + width,
2614
+ y + height,
2615
+ x,
2616
+ y + height
2617
+ ];
2618
+ let count = 0;
2619
+ verticesOffset *= verticesStride;
2620
+ vertices[verticesOffset + count] = points[0];
2621
+ vertices[verticesOffset + count + 1] = points[1];
2622
+ count += verticesStride;
2623
+ vertices[verticesOffset + count] = points[2];
2624
+ vertices[verticesOffset + count + 1] = points[3];
2625
+ count += verticesStride;
2626
+ vertices[verticesOffset + count] = points[6];
2627
+ vertices[verticesOffset + count + 1] = points[7];
2628
+ count += verticesStride;
2629
+ vertices[verticesOffset + count] = points[4];
2630
+ vertices[verticesOffset + count + 1] = points[5];
2631
+ count += verticesStride;
2632
+ const verticesIndex = verticesOffset / verticesStride;
2633
+ indices[indicesOffset++] = verticesIndex;
2634
+ indices[indicesOffset++] = verticesIndex + 1;
2635
+ indices[indicesOffset++] = verticesIndex + 2;
2636
+ indices[indicesOffset++] = verticesIndex + 1;
2637
+ indices[indicesOffset++] = verticesIndex + 3;
2638
+ indices[indicesOffset++] = verticesIndex + 2;
2639
+ return { vertices, indices };
1950
2640
  }
1951
2641
  }
1952
2642
 
@@ -1970,7 +2660,7 @@ class SplineCurve extends Curve {
1970
2660
  );
1971
2661
  return output;
1972
2662
  }
1973
- getControlPoints() {
2663
+ getControlPointRefs() {
1974
2664
  return this.points;
1975
2665
  }
1976
2666
  copy(source) {
@@ -1983,22 +2673,16 @@ class SplineCurve extends Curve {
1983
2673
  }
1984
2674
  }
1985
2675
 
1986
- class CurvePath extends Curve {
1987
- curves = [];
2676
+ class CurvePath extends CompositeCurve {
1988
2677
  startPoint;
1989
2678
  currentPoint;
1990
2679
  autoClose = false;
1991
- _cacheLengths = [];
1992
2680
  constructor(points) {
1993
2681
  super();
1994
2682
  if (points) {
1995
2683
  this.addPoints(points);
1996
2684
  }
1997
2685
  }
1998
- addCurve(curve) {
1999
- this.curves.push(curve);
2000
- return this;
2001
- }
2002
2686
  addPoints(points) {
2003
2687
  this.moveTo(points[0].x, points[0].y);
2004
2688
  for (let i = 1, len = points.length; i < len; i++) {
@@ -2008,82 +2692,26 @@ class CurvePath extends Curve {
2008
2692
  return this;
2009
2693
  }
2010
2694
  addCommands(commands) {
2011
- addPathCommandsToPath2D(commands, this);
2695
+ svgPathCommandsAddToPath2D(commands, this);
2012
2696
  return this;
2013
2697
  }
2014
2698
  addData(data) {
2015
- this.addCommands(pathDataToPathCommands(data));
2699
+ this.addCommands(svgPathDataToCommands(data));
2016
2700
  return this;
2017
2701
  }
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++;
2702
+ getUnevenPointArray(count = 40, output = []) {
2703
+ super.getUnevenPointArray(count, output);
2704
+ if (this.autoClose && output.length >= 4 && (output[0] !== output[output.length - 2] && output[1] !== output[output.length - 1])) {
2705
+ output.push(output[0], output[1]);
2030
2706
  }
2031
2707
  return output;
2032
2708
  }
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
- }
2709
+ getSpacedPointArray(count = 40, output = []) {
2710
+ super.getSpacedPointArray(count, output);
2711
+ if (this.autoClose && output.length >= 4 && (output[0] !== output[output.length - 2] && output[1] !== output[output.length - 1])) {
2712
+ output.push(output[0], output[1]);
2082
2713
  }
2083
- if (this.autoClose && points.length > 1 && !points[points.length - 1].equals(points[0])) {
2084
- points.push(points[0]);
2085
- }
2086
- return points;
2714
+ return output;
2087
2715
  }
2088
2716
  _setCurrentPoint(point) {
2089
2717
  this.currentPoint = new Vector2(point.x, point.y);
@@ -2092,12 +2720,21 @@ class CurvePath extends Curve {
2092
2720
  }
2093
2721
  return this;
2094
2722
  }
2723
+ _connetLineTo(curve) {
2724
+ if (this.curves.length > 0) {
2725
+ const first = curve.getPoint(0);
2726
+ if (!this.currentPoint || !first.equals(this.currentPoint)) {
2727
+ this.lineTo(first.x, first.y);
2728
+ }
2729
+ }
2730
+ return this;
2731
+ }
2095
2732
  closePath() {
2096
2733
  const start = this.startPoint;
2097
2734
  if (start) {
2098
2735
  const end = this.currentPoint;
2099
2736
  if (end && !start.equals(end)) {
2100
- this.curves.push(new LineCurve(end.clone(), start));
2737
+ this.curves.push(LineCurve.from(end, start));
2101
2738
  end.copy(start);
2102
2739
  }
2103
2740
  this.startPoint = void 0;
@@ -2114,8 +2751,10 @@ class CurvePath extends Curve {
2114
2751
  if (!start?.equals({ x, y })) {
2115
2752
  this.curves.push(
2116
2753
  new LineCurve(
2117
- start?.clone() ?? new Vector2(),
2118
- new Vector2(x, y)
2754
+ start?.x ?? 0,
2755
+ start?.y ?? 0,
2756
+ x,
2757
+ y
2119
2758
  )
2120
2759
  );
2121
2760
  }
@@ -2127,10 +2766,14 @@ class CurvePath extends Curve {
2127
2766
  if (!start?.equals({ x, y })) {
2128
2767
  this.curves.push(
2129
2768
  new CubicBezierCurve(
2130
- start?.clone() ?? new Vector2(),
2131
- new Vector2(cp1x, cp1y),
2132
- new Vector2(cp2x, cp2y),
2133
- new Vector2(x, y)
2769
+ start?.x ?? 0,
2770
+ start?.y ?? 0,
2771
+ cp1x,
2772
+ cp1y,
2773
+ cp2x,
2774
+ cp2y,
2775
+ x,
2776
+ y
2134
2777
  )
2135
2778
  );
2136
2779
  }
@@ -2142,9 +2785,12 @@ class CurvePath extends Curve {
2142
2785
  if (!start?.equals({ x, y })) {
2143
2786
  this.curves.push(
2144
2787
  new QuadraticBezierCurve(
2145
- start?.clone() ?? new Vector2(),
2146
- new Vector2(cpx, cpy),
2147
- new Vector2(x, y)
2788
+ start?.x ?? 0,
2789
+ start?.y ?? 0,
2790
+ cpx,
2791
+ cpy,
2792
+ x,
2793
+ y
2148
2794
  )
2149
2795
  );
2150
2796
  }
@@ -2152,12 +2798,23 @@ class CurvePath extends Curve {
2152
2798
  return this;
2153
2799
  }
2154
2800
  arc(x, y, radius, startAngle, endAngle, counterclockwise) {
2155
- this.ellipse(x, y, radius, radius, 0, startAngle, endAngle, counterclockwise);
2801
+ const curve = new ArcCurve(
2802
+ x,
2803
+ y,
2804
+ radius,
2805
+ startAngle,
2806
+ endAngle,
2807
+ !counterclockwise
2808
+ );
2809
+ this._connetLineTo(curve);
2810
+ this.curves.push(curve);
2811
+ this._setCurrentPoint(curve.getPoint(1));
2156
2812
  return this;
2157
2813
  }
2158
2814
  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);
2815
+ x += this.currentPoint?.x ?? 0;
2816
+ y += this.currentPoint?.y ?? 0;
2817
+ this.arc(x, y, radius, startAngle, endAngle, counterclockwise);
2161
2818
  return this;
2162
2819
  }
2163
2820
  // TODO
@@ -2168,7 +2825,8 @@ class CurvePath extends Curve {
2168
2825
  }
2169
2826
  ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise = true) {
2170
2827
  const curve = new EllipseCurve(
2171
- new Vector2(x, y),
2828
+ x,
2829
+ y,
2172
2830
  radiusX,
2173
2831
  radiusY,
2174
2832
  rotation,
@@ -2176,29 +2834,21 @@ class CurvePath extends Curve {
2176
2834
  endAngle,
2177
2835
  !counterclockwise
2178
2836
  );
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
- }
2837
+ this._connetLineTo(curve);
2185
2838
  this.curves.push(curve);
2186
2839
  this._setCurrentPoint(curve.getPoint(1));
2187
2840
  return this;
2188
2841
  }
2189
2842
  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);
2843
+ x += this.currentPoint?.x ?? 0;
2844
+ y += this.currentPoint?.y ?? 0;
2845
+ this.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise);
2192
2846
  return this;
2193
2847
  }
2194
2848
  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
- );
2849
+ const curve = new RectangleCurve(x, y, w, h);
2850
+ this._connetLineTo(curve);
2851
+ this.curves.push(curve);
2202
2852
  this._setCurrentPoint({ x, y });
2203
2853
  return this;
2204
2854
  }
@@ -2208,17 +2858,6 @@ class CurvePath extends Curve {
2208
2858
  this._setCurrentPoint(points[points.length - 1]);
2209
2859
  return this;
2210
2860
  }
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
2861
  drawTo(ctx) {
2223
2862
  const point = this.curves[0]?.getPoint(0);
2224
2863
  if (point) {
@@ -2269,20 +2908,22 @@ function getIntersectionPoint(p1, p2, q1, q2) {
2269
2908
  );
2270
2909
  }
2271
2910
 
2272
- class Path2D {
2273
- currentPath = new CurvePath();
2274
- paths = [this.currentPath];
2911
+ class Path2D extends CompositeCurve {
2912
+ currentCurve = new CurvePath();
2275
2913
  style;
2276
2914
  get startPoint() {
2277
- return this.currentPath.startPoint;
2915
+ return this.currentCurve.startPoint;
2278
2916
  }
2279
2917
  get currentPoint() {
2280
- return this.currentPath.currentPoint;
2918
+ return this.currentCurve.currentPoint;
2281
2919
  }
2282
2920
  get strokeWidth() {
2283
2921
  return this.style.strokeWidth ?? ((this.style.stroke ?? "none") === "none" ? 0 : 1);
2284
2922
  }
2285
2923
  constructor(path, style = {}) {
2924
+ super();
2925
+ this.curves.push(this.currentCurve);
2926
+ this.style = style;
2286
2927
  if (path) {
2287
2928
  if (path instanceof Path2D) {
2288
2929
  this.addPath(path);
@@ -2292,99 +2933,90 @@ class Path2D {
2292
2933
  this.addData(path);
2293
2934
  }
2294
2935
  }
2295
- this.style = style;
2296
2936
  }
2297
2937
  addPath(path) {
2298
2938
  if (path instanceof Path2D) {
2299
- this.paths.push(...path.paths.map((v) => v.clone()));
2939
+ this.curves.push(...path.curves.map((v) => v.clone()));
2300
2940
  } else {
2301
- this.paths.push(path);
2941
+ this.curves.push(path);
2302
2942
  }
2303
2943
  return this;
2304
2944
  }
2305
2945
  closePath() {
2306
2946
  const startPoint = this.startPoint;
2307
2947
  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);
2948
+ this.currentCurve.closePath();
2949
+ if (this.currentCurve.curves.length > 0) {
2950
+ this.currentCurve = new CurvePath().moveTo(startPoint.x, startPoint.y);
2951
+ this.curves.push(this.currentCurve);
2312
2952
  }
2313
2953
  }
2314
2954
  return this;
2315
2955
  }
2316
2956
  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);
2957
+ if (!this.currentCurve.currentPoint?.equals({ x, y })) {
2958
+ if (this.currentCurve.curves.length) {
2959
+ this.currentCurve = new CurvePath();
2960
+ this.curves.push(this.currentCurve);
2324
2961
  }
2962
+ this.currentCurve.moveTo(x, y);
2325
2963
  }
2326
2964
  return this;
2327
2965
  }
2328
2966
  lineTo(x, y) {
2329
- this.currentPath.lineTo(x, y);
2967
+ this.currentCurve.lineTo(x, y);
2330
2968
  return this;
2331
2969
  }
2332
2970
  bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
2333
- this.currentPath.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
2971
+ this.currentCurve.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
2334
2972
  return this;
2335
2973
  }
2336
2974
  quadraticCurveTo(cpx, cpy, x, y) {
2337
- this.currentPath.quadraticCurveTo(cpx, cpy, x, y);
2975
+ this.currentCurve.quadraticCurveTo(cpx, cpy, x, y);
2338
2976
  return this;
2339
2977
  }
2340
2978
  arc(x, y, radius, startAngle, endAngle, counterclockwise) {
2341
- this.currentPath.arc(x, y, radius, startAngle, endAngle, counterclockwise);
2979
+ this.currentCurve.arc(x, y, radius, startAngle, endAngle, counterclockwise);
2342
2980
  return this;
2343
2981
  }
2344
2982
  arcTo(x1, y1, x2, y2, radius) {
2345
- this.currentPath.arcTo(x1, y1, x2, y2, radius);
2983
+ this.currentCurve.arcTo(x1, y1, x2, y2, radius);
2346
2984
  return this;
2347
2985
  }
2348
2986
  ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) {
2349
- this.currentPath.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise);
2987
+ this.currentCurve.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise);
2350
2988
  return this;
2351
2989
  }
2352
2990
  rect(x, y, w, h) {
2353
- this.currentPath.rect(x, y, w, h);
2991
+ this.currentCurve.rect(x, y, w, h);
2354
2992
  return this;
2355
2993
  }
2356
2994
  addCommands(commands) {
2357
- addPathCommandsToPath2D(commands, this);
2995
+ svgPathCommandsAddToPath2D(commands, this);
2358
2996
  return this;
2359
2997
  }
2360
2998
  addData(data) {
2361
- this.addCommands(pathDataToPathCommands(data));
2999
+ this.addCommands(svgPathDataToCommands(data));
2362
3000
  return this;
2363
3001
  }
2364
3002
  splineThru(points) {
2365
- this.currentPath.splineThru(points);
3003
+ this.currentCurve.splineThru(points);
2366
3004
  return this;
2367
3005
  }
2368
- getControlPoints() {
2369
- return this.paths.flatMap((path) => path.getControlPoints());
2370
- }
2371
- getCurves() {
2372
- return this.paths.flatMap((path) => path.curves);
2373
- }
2374
3006
  scale(sx, sy = sx, target = { x: 0, y: 0 }) {
2375
- this.getControlPoints().forEach((point) => {
3007
+ this.getControlPointRefs().forEach((point) => {
2376
3008
  point.scale(sx, sy, target);
2377
3009
  });
2378
3010
  return this;
2379
3011
  }
2380
3012
  skew(ax, ay = 0, target = { x: 0, y: 0 }) {
2381
- this.getControlPoints().forEach((point) => {
3013
+ this.getControlPointRefs().forEach((point) => {
2382
3014
  point.skew(ax, ay, target);
2383
3015
  });
2384
3016
  return this;
2385
3017
  }
2386
3018
  rotate(a, target = { x: 0, y: 0 }) {
2387
- this.getControlPoints().forEach((point) => {
3019
+ this.getControlPointRefs().forEach((point) => {
2388
3020
  point.rotate(a, target);
2389
3021
  });
2390
3022
  return this;
@@ -2393,12 +3025,14 @@ class Path2D {
2393
3025
  if (b === 0) {
2394
3026
  return this;
2395
3027
  }
2396
- const curves = this.getCurves();
3028
+ const curves = this.curves;
2397
3029
  const _list = [];
2398
3030
  const _isClockwise = [];
2399
3031
  const _points = [];
2400
3032
  curves.forEach((curve, index) => {
2401
- const points = curve.getControlPoints();
3033
+ if (!curve.getLength())
3034
+ return;
3035
+ const points = curve.getControlPointRefs();
2402
3036
  const isClockwise = curve.isClockwise();
2403
3037
  _points[index] = points;
2404
3038
  _isClockwise[index] = isClockwise;
@@ -2420,6 +3054,8 @@ class Path2D {
2420
3054
  });
2421
3055
  });
2422
3056
  curves.forEach((curve, index) => {
3057
+ if (!curve.getLength())
3058
+ return;
2423
3059
  const isClockwise = _isClockwise[index];
2424
3060
  const points = _points[index];
2425
3061
  points.forEach((point) => {
@@ -2447,19 +3083,19 @@ class Path2D {
2447
3083
  return this;
2448
3084
  }
2449
3085
  matrix(matrix) {
2450
- this.getCurves().forEach((curve) => curve.matrix(matrix));
3086
+ this.curves.forEach((curve) => curve.matrix(matrix));
2451
3087
  return this;
2452
3088
  }
2453
3089
  getMinMax(min = Vector2.MAX, max = Vector2.MIN, withStyle = true) {
2454
3090
  const strokeWidth = this.strokeWidth;
2455
- this.getCurves().forEach((curve) => {
3091
+ this.curves.forEach((curve) => {
2456
3092
  curve.getMinMax(min, max);
2457
3093
  if (withStyle) {
2458
3094
  if (strokeWidth > 1) {
2459
3095
  const halfStrokeWidth = strokeWidth / 2;
2460
3096
  const isClockwise = curve.isClockwise();
2461
3097
  const points = [];
2462
- for (let t = 0; t <= 1; t += 1 / curve.arcLengthDivisions) {
3098
+ for (let t = 0; t <= 1; t += 1 / curve.arcLengthDivision) {
2463
3099
  const point = curve.getPoint(t);
2464
3100
  const normal = curve.getNormal(t);
2465
3101
  const dist1 = normal.clone().scale(isClockwise ? halfStrokeWidth : -halfStrokeWidth);
@@ -2492,7 +3128,7 @@ class Path2D {
2492
3128
  ctx.beginPath();
2493
3129
  ctx.save();
2494
3130
  setCanvasContext(ctx, style);
2495
- this.paths.forEach((path) => {
3131
+ this.curves.forEach((path) => {
2496
3132
  path.drawTo(ctx);
2497
3133
  });
2498
3134
  if (fill !== "none") {
@@ -2510,9 +3146,8 @@ class Path2D {
2510
3146
  ctx.beginPath();
2511
3147
  ctx.save();
2512
3148
  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);
3149
+ this.getControlPointRefs().forEach((point) => {
3150
+ drawPoint(ctx, point.x, point.y, { radius: 4 });
2516
3151
  });
2517
3152
  if (fill !== "none") {
2518
3153
  ctx.fill();
@@ -2524,10 +3159,10 @@ class Path2D {
2524
3159
  return this;
2525
3160
  }
2526
3161
  toCommands() {
2527
- return this.paths.flatMap((path) => path.toCommands());
3162
+ return this.curves.flatMap((v) => v.toCommands());
2528
3163
  }
2529
3164
  toData() {
2530
- return this.paths.map((path) => path.toData()).join(" ");
3165
+ return this.curves.filter((v) => v.curves.length).map((v) => v.toData()).join(" ");
2531
3166
  }
2532
3167
  toSVGPathString() {
2533
3168
  const style = {
@@ -2552,10 +3187,34 @@ class Path2D {
2552
3187
  }
2553
3188
  return `<path d="${this.toData()}" style="${cssText}"></path>`;
2554
3189
  }
3190
+ copy(source) {
3191
+ this.currentCurve = source.currentCurve.clone();
3192
+ this.curves = source.curves.map((path) => path.clone());
3193
+ this.style = { ...source.style };
3194
+ return this;
3195
+ }
3196
+ clone() {
3197
+ return new this.constructor().copy(this);
3198
+ }
3199
+ }
3200
+
3201
+ class Path2DSet {
3202
+ constructor(paths = []) {
3203
+ this.paths = paths;
3204
+ }
3205
+ getBoundingBox(withStyle = true) {
3206
+ if (!this.paths.length) {
3207
+ return void 0;
3208
+ }
3209
+ const min = Vector2.MAX;
3210
+ const max = Vector2.MIN;
3211
+ this.paths.forEach((path) => path.getMinMax(min, max, withStyle));
3212
+ return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
3213
+ }
2555
3214
  toSVGString() {
2556
3215
  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>`;
3216
+ const content = this.paths.map((path) => path.toSVGPathString()).join("");
3217
+ return `<svg viewBox="${x} ${y} ${width} ${height}" width="${width}px" height="${height}px" xmlns="http://www.w3.org/2000/svg">${content}</svg>`;
2559
3218
  }
2560
3219
  toSVGUrl() {
2561
3220
  return `data:image/svg+xml;base64,${btoa(this.toSVGString())}`;
@@ -2575,19 +3234,12 @@ class Path2D {
2575
3234
  if (ctx) {
2576
3235
  ctx.scale(pixelRatio, pixelRatio);
2577
3236
  ctx.translate(-left, -top);
2578
- this.drawTo(ctx, style);
3237
+ this.paths.forEach((path) => {
3238
+ path.drawTo(ctx, style);
3239
+ });
2579
3240
  }
2580
3241
  return canvas;
2581
3242
  }
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);
2590
- }
2591
3243
  }
2592
3244
 
2593
3245
  const defaultUnit = "px";
@@ -3116,73 +3768,38 @@ ${xml}`);
3116
3768
  }
3117
3769
  }
3118
3770
  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;
3771
+ return new Path2DSet(parseNode(parseSVGToDOM(svg), {}));
3159
3772
  }
3160
3773
 
3774
+ exports.ArcCurve = ArcCurve;
3161
3775
  exports.BoundingBox = BoundingBox;
3162
- exports.CircleCurve = CircleCurve;
3776
+ exports.CompositeCurve = CompositeCurve;
3163
3777
  exports.CubicBezierCurve = CubicBezierCurve;
3164
3778
  exports.Curve = Curve;
3165
3779
  exports.CurvePath = CurvePath;
3166
3780
  exports.EllipseCurve = EllipseCurve;
3167
- exports.HeartCurve = HeartCurve;
3168
3781
  exports.LineCurve = LineCurve;
3169
3782
  exports.Matrix3 = Matrix3;
3170
3783
  exports.Path2D = Path2D;
3784
+ exports.Path2DSet = Path2DSet;
3171
3785
  exports.PloygonCurve = PloygonCurve;
3172
3786
  exports.QuadraticBezierCurve = QuadraticBezierCurve;
3173
- exports.RectangularCurve = RectangularCurve;
3787
+ exports.RectangleCurve = RectangleCurve;
3174
3788
  exports.SplineCurve = SplineCurve;
3175
3789
  exports.Vector2 = Vector2;
3176
- exports.addPathCommandsToPath2D = addPathCommandsToPath2D;
3177
- exports.getPathsBoundingBox = getPathsBoundingBox;
3790
+ exports.catmullRom = catmullRom;
3791
+ exports.cubicBezier = cubicBezier;
3792
+ exports.drawPoint = drawPoint;
3793
+ exports.fillTriangulate = fillTriangulate;
3794
+ exports.getAdaptiveCubicBezierCurvePoints = getAdaptiveCubicBezierCurvePoints;
3795
+ exports.getAdaptiveQuadraticBezierCurvePoints = getAdaptiveQuadraticBezierCurvePoints;
3178
3796
  exports.parseArcCommand = parseArcCommand;
3179
3797
  exports.parsePathDataArgs = parsePathDataArgs;
3180
3798
  exports.parseSVG = parseSVG;
3181
3799
  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;
3800
+ exports.quadraticBezier = quadraticBezier;
3188
3801
  exports.setCanvasContext = setCanvasContext;
3802
+ exports.strokeTriangulate = strokeTriangulate;
3803
+ exports.svgPathCommandsAddToPath2D = svgPathCommandsAddToPath2D;
3804
+ exports.svgPathCommandsToData = svgPathCommandsToData;
3805
+ exports.svgPathDataToCommands = svgPathDataToCommands;