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