modern-path2d 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/index.cjs +1517 -900
- package/dist/index.d.cts +253 -254
- package/dist/index.d.mts +253 -254
- package/dist/index.d.ts +253 -254
- package/dist/index.js +2 -2
- package/dist/index.mjs +1499 -890
- 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,684 @@ 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
|
-
const
|
|
1008
|
-
for (let i = 0; i <=
|
|
1009
|
-
|
|
1592
|
+
getUnevenPointArray(count = 5, output = []) {
|
|
1593
|
+
const p = new Vector2();
|
|
1594
|
+
for (let i = 0, len = Math.max(1, count) - 1; i <= len; i++) {
|
|
1595
|
+
this.getPoint(i / len, p);
|
|
1596
|
+
output.push(p.x, p.y);
|
|
1010
1597
|
}
|
|
1011
|
-
return
|
|
1598
|
+
return output;
|
|
1012
1599
|
}
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1600
|
+
getSpacedPointArray(count = 5, output = []) {
|
|
1601
|
+
const p = new Vector2();
|
|
1602
|
+
for (let i = 0, len = Math.max(1, count) - 1; i <= len; i++) {
|
|
1603
|
+
this.getPointAt(i / len, p);
|
|
1604
|
+
output.push(p.x, p.y);
|
|
1605
|
+
}
|
|
1606
|
+
return output;
|
|
1016
1607
|
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1608
|
+
getAdaptivePointArray(output = []) {
|
|
1609
|
+
return this.getUnevenPointArray(5, output);
|
|
1610
|
+
}
|
|
1611
|
+
_pointArrayToPoint(array, output = []) {
|
|
1612
|
+
for (let i = 0, len = array.length; i < len; i += 2) {
|
|
1613
|
+
const x = array[i];
|
|
1614
|
+
const y = array[i + 1];
|
|
1615
|
+
output.push(new Vector2(x, y));
|
|
1616
|
+
}
|
|
1617
|
+
return output;
|
|
1618
|
+
}
|
|
1619
|
+
getSpacedPoints(count, output = []) {
|
|
1620
|
+
const array = this.getSpacedPointArray(count);
|
|
1621
|
+
this._pointArrayToPoint(array, output);
|
|
1622
|
+
return output;
|
|
1623
|
+
}
|
|
1624
|
+
getUnevenPoints(count, output = []) {
|
|
1625
|
+
const array = this.getUnevenPointArray(count);
|
|
1626
|
+
this._pointArrayToPoint(array, output);
|
|
1627
|
+
return output;
|
|
1628
|
+
}
|
|
1629
|
+
getAdaptivePoints(output = []) {
|
|
1630
|
+
const array = this.getAdaptivePointArray();
|
|
1631
|
+
this._pointArrayToPoint(array, output);
|
|
1632
|
+
return output;
|
|
1633
|
+
}
|
|
1634
|
+
getPoints(count, output = []) {
|
|
1635
|
+
let array;
|
|
1636
|
+
if (count) {
|
|
1637
|
+
array = this.getUnevenPointArray(count);
|
|
1638
|
+
} else {
|
|
1639
|
+
array = this.getAdaptivePointArray();
|
|
1021
1640
|
}
|
|
1022
|
-
|
|
1641
|
+
this._pointArrayToPoint(array, output);
|
|
1642
|
+
return output;
|
|
1023
1643
|
}
|
|
1024
1644
|
getLength() {
|
|
1025
1645
|
const lengths = this.getLengths();
|
|
1026
1646
|
return lengths[lengths.length - 1];
|
|
1027
1647
|
}
|
|
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();
|
|
1648
|
+
getLengths() {
|
|
1649
|
+
if (!this._arcLengths || this._arcLengths.length !== this.arcLengthDivision + 1) {
|
|
1650
|
+
this.updateLengths();
|
|
1651
|
+
}
|
|
1652
|
+
return this._arcLengths;
|
|
1653
|
+
}
|
|
1654
|
+
updateLengths() {
|
|
1655
|
+
const divisions = this.arcLengthDivision;
|
|
1656
|
+
const arcLengths = [0];
|
|
1657
|
+
for (let sum = 0, prev = this.getPoint(0), i = 1; i <= divisions; i++) {
|
|
1658
|
+
const current = this.getPoint(i / divisions);
|
|
1659
|
+
sum += current.distanceTo(prev);
|
|
1660
|
+
arcLengths.push(sum);
|
|
1661
|
+
prev = current;
|
|
1662
|
+
}
|
|
1663
|
+
this._arcLengths = arcLengths;
|
|
1050
1664
|
}
|
|
1051
1665
|
getUToTMapping(u, distance) {
|
|
1052
1666
|
const lengths = this.getLengths();
|
|
1053
|
-
let i = 0;
|
|
1054
1667
|
const lengthsLen = lengths.length;
|
|
1055
|
-
|
|
1056
|
-
if (
|
|
1057
|
-
|
|
1058
|
-
} else {
|
|
1059
|
-
targetLength = u * lengths[lengthsLen - 1];
|
|
1668
|
+
const targetLen = distance ?? u * lengths[lengthsLen - 1];
|
|
1669
|
+
if (lengthsLen < 2) {
|
|
1670
|
+
return targetLen / lengths[0];
|
|
1060
1671
|
}
|
|
1672
|
+
let i = 0;
|
|
1061
1673
|
let low = 0;
|
|
1062
1674
|
let high = lengthsLen - 1;
|
|
1063
1675
|
let comparison;
|
|
1064
1676
|
while (low <= high) {
|
|
1065
1677
|
i = Math.floor(low + (high - low) / 2);
|
|
1066
|
-
comparison = lengths[i] -
|
|
1678
|
+
comparison = lengths[i] - targetLen;
|
|
1067
1679
|
if (comparison < 0) {
|
|
1068
1680
|
low = i + 1;
|
|
1069
1681
|
} else if (comparison > 0) {
|
|
@@ -1074,13 +1686,13 @@ class Curve {
|
|
|
1074
1686
|
}
|
|
1075
1687
|
}
|
|
1076
1688
|
i = high;
|
|
1077
|
-
if (lengths[i] ===
|
|
1689
|
+
if (lengths[i] === targetLen) {
|
|
1078
1690
|
return i / (lengthsLen - 1);
|
|
1079
1691
|
}
|
|
1080
|
-
const
|
|
1081
|
-
const
|
|
1082
|
-
const segmentLength =
|
|
1083
|
-
const segmentFraction = (
|
|
1692
|
+
const before = lengths[i];
|
|
1693
|
+
const after = lengths[i + 1];
|
|
1694
|
+
const segmentLength = after - before;
|
|
1695
|
+
const segmentFraction = (targetLen - before) / segmentLength;
|
|
1084
1696
|
return (i + segmentFraction) / (lengthsLen - 1);
|
|
1085
1697
|
}
|
|
1086
1698
|
getTangent(t, output = new Vector2()) {
|
|
@@ -1119,31 +1731,55 @@ class Curve {
|
|
|
1119
1731
|
return mid;
|
|
1120
1732
|
}
|
|
1121
1733
|
matrix(matrix) {
|
|
1122
|
-
this.
|
|
1734
|
+
this.getControlPointRefs().forEach((point) => point.applyMatrix3(matrix));
|
|
1123
1735
|
return this;
|
|
1124
1736
|
}
|
|
1125
1737
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1126
|
-
this.getPoints()
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1738
|
+
const potins = this.getPoints();
|
|
1739
|
+
for (let i = 0, len = potins.length; i < len; i++) {
|
|
1740
|
+
const p = potins[i];
|
|
1741
|
+
min.min(p);
|
|
1742
|
+
max.max(p);
|
|
1743
|
+
}
|
|
1130
1744
|
return { min, max };
|
|
1131
1745
|
}
|
|
1132
1746
|
getBoundingBox() {
|
|
1133
1747
|
const { min, max } = this.getMinMax();
|
|
1134
1748
|
return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
1135
1749
|
}
|
|
1750
|
+
fillTriangulate(options) {
|
|
1751
|
+
return fillTriangulate(
|
|
1752
|
+
this.getPoints().reduce((arr, p) => {
|
|
1753
|
+
arr.push(p.x, p.y);
|
|
1754
|
+
return arr;
|
|
1755
|
+
}, []),
|
|
1756
|
+
options
|
|
1757
|
+
);
|
|
1758
|
+
}
|
|
1759
|
+
strokeTriangulate(options) {
|
|
1760
|
+
return strokeTriangulate(
|
|
1761
|
+
this.getPoints().reduce((arr, p) => {
|
|
1762
|
+
arr.push(p.x, p.y);
|
|
1763
|
+
return arr;
|
|
1764
|
+
}, []),
|
|
1765
|
+
options
|
|
1766
|
+
);
|
|
1767
|
+
}
|
|
1136
1768
|
toCommands() {
|
|
1137
|
-
|
|
1769
|
+
const comds = [];
|
|
1770
|
+
const potins = this.getPoints();
|
|
1771
|
+
for (let i = 0, len = potins.length; i < len; i++) {
|
|
1772
|
+
const p = potins[i];
|
|
1138
1773
|
if (i === 0) {
|
|
1139
|
-
|
|
1774
|
+
comds.push({ type: "M", x: p.x, y: p.y });
|
|
1140
1775
|
} else {
|
|
1141
|
-
|
|
1776
|
+
comds.push({ type: "L", x: p.x, y: p.y });
|
|
1142
1777
|
}
|
|
1143
|
-
}
|
|
1778
|
+
}
|
|
1779
|
+
return comds;
|
|
1144
1780
|
}
|
|
1145
1781
|
toData() {
|
|
1146
|
-
return
|
|
1782
|
+
return svgPathCommandsToData(this.toCommands());
|
|
1147
1783
|
}
|
|
1148
1784
|
drawTo(ctx) {
|
|
1149
1785
|
this.toCommands().forEach((cmd) => {
|
|
@@ -1159,7 +1795,7 @@ class Curve {
|
|
|
1159
1795
|
return this;
|
|
1160
1796
|
}
|
|
1161
1797
|
copy(source) {
|
|
1162
|
-
this.
|
|
1798
|
+
this.arcLengthDivision = source.arcLengthDivision;
|
|
1163
1799
|
return this;
|
|
1164
1800
|
}
|
|
1165
1801
|
clone() {
|
|
@@ -1167,179 +1803,22 @@ class Curve {
|
|
|
1167
1803
|
}
|
|
1168
1804
|
}
|
|
1169
1805
|
|
|
1170
|
-
class CircleCurve extends Curve {
|
|
1171
|
-
constructor(center, radius, start = 0, end = Math.PI * 2) {
|
|
1172
|
-
super();
|
|
1173
|
-
this.center = center;
|
|
1174
|
-
this.radius = radius;
|
|
1175
|
-
this.start = start;
|
|
1176
|
-
this.end = end;
|
|
1177
|
-
}
|
|
1178
|
-
getPoint(t) {
|
|
1179
|
-
const { radius, center } = this;
|
|
1180
|
-
return center.clone().add(this.getNormal(t).clone().scale(radius));
|
|
1181
|
-
}
|
|
1182
|
-
getTangent(t, output = new Vector2()) {
|
|
1183
|
-
const { x, y } = this.getNormal(t);
|
|
1184
|
-
return output.set(-y, x);
|
|
1185
|
-
}
|
|
1186
|
-
getNormal(t, output = new Vector2()) {
|
|
1187
|
-
const { start, end } = this;
|
|
1188
|
-
const _t = t * (end - start) + start - 0.5 * Math.PI;
|
|
1189
|
-
return output.set(Math.cos(_t), Math.sin(_t));
|
|
1190
|
-
}
|
|
1191
|
-
getControlPoints() {
|
|
1192
|
-
return [this.center];
|
|
1193
|
-
}
|
|
1194
|
-
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1195
|
-
min.x = Math.min(min.x, this.center.x - this.radius);
|
|
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;
|
|
1248
|
-
}
|
|
1249
|
-
getPoint(t, output = new Vector2()) {
|
|
1250
|
-
const { start, startControl, endControl, end } = this;
|
|
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
|
-
);
|
|
1255
|
-
}
|
|
1256
|
-
getControlPoints() {
|
|
1257
|
-
return [
|
|
1258
|
-
this.start,
|
|
1259
|
-
this.startControl,
|
|
1260
|
-
this.endControl,
|
|
1261
|
-
this.end
|
|
1262
|
-
];
|
|
1263
|
-
}
|
|
1264
|
-
_solveQuadratic(a, b, c) {
|
|
1265
|
-
const discriminant = b * b - 4 * a * c;
|
|
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);
|
|
1272
|
-
}
|
|
1273
|
-
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1274
|
-
const p0 = this.start;
|
|
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 };
|
|
1304
|
-
}
|
|
1305
|
-
toCommands() {
|
|
1306
|
-
const { start, startControl, endControl, end } = this;
|
|
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
|
-
];
|
|
1311
|
-
}
|
|
1312
|
-
drawTo(ctx) {
|
|
1313
|
-
const { start, startControl, endControl, end } = this;
|
|
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;
|
|
1317
|
-
}
|
|
1318
|
-
copy(source) {
|
|
1319
|
-
super.copy(source);
|
|
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;
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
1806
|
const tempTransform0$1 = new Matrix3();
|
|
1329
1807
|
const tempTransform1$1 = new Matrix3();
|
|
1330
1808
|
const tempTransform2$1 = new Matrix3();
|
|
1331
1809
|
const tempV2 = new Vector2();
|
|
1332
1810
|
class EllipseCurve extends Curve {
|
|
1333
|
-
constructor(
|
|
1811
|
+
constructor(cx = 0, cy = 0, rx = 1, ry = 1, rotation = 0, startAngle = 0, endAngle = Math.PI * 2, clockwise = false) {
|
|
1334
1812
|
super();
|
|
1335
|
-
this.center = center;
|
|
1336
|
-
this.radiusX = radiusX;
|
|
1337
|
-
this.radiusY = radiusY;
|
|
1338
1813
|
this.rotation = rotation;
|
|
1339
1814
|
this.startAngle = startAngle;
|
|
1340
1815
|
this.endAngle = endAngle;
|
|
1341
1816
|
this.clockwise = clockwise;
|
|
1817
|
+
this.center = new Vector2(cx, cy);
|
|
1818
|
+
this.radius = new Vector2(rx, ry);
|
|
1342
1819
|
}
|
|
1820
|
+
center;
|
|
1821
|
+
radius;
|
|
1343
1822
|
isClockwise() {
|
|
1344
1823
|
return this.clockwise;
|
|
1345
1824
|
}
|
|
@@ -1364,8 +1843,8 @@ class EllipseCurve extends Curve {
|
|
|
1364
1843
|
}
|
|
1365
1844
|
}
|
|
1366
1845
|
const angle = this.startAngle + t * deltaAngle;
|
|
1367
|
-
let _x = this.center.x + this.
|
|
1368
|
-
let _y = this.center.y + this.
|
|
1846
|
+
let _x = this.center.x + this.radius.x * Math.cos(angle);
|
|
1847
|
+
let _y = this.center.y + this.radius.y * Math.sin(angle);
|
|
1369
1848
|
if (this.rotation !== 0) {
|
|
1370
1849
|
const cos = Math.cos(this.rotation);
|
|
1371
1850
|
const sin = Math.sin(this.rotation);
|
|
@@ -1377,8 +1856,9 @@ class EllipseCurve extends Curve {
|
|
|
1377
1856
|
return output.set(_x, _y);
|
|
1378
1857
|
}
|
|
1379
1858
|
toCommands() {
|
|
1380
|
-
const { center,
|
|
1859
|
+
const { center, radius, startAngle, endAngle, clockwise, rotation } = this;
|
|
1381
1860
|
const { x: cx, y: cy } = center;
|
|
1861
|
+
const { x: rx, y: ry } = radius;
|
|
1382
1862
|
const startX = cx + rx * Math.cos(startAngle) * Math.cos(rotation) - ry * Math.sin(startAngle) * Math.sin(rotation);
|
|
1383
1863
|
const startY = cy + rx * Math.cos(startAngle) * Math.sin(rotation) + ry * Math.sin(startAngle) * Math.cos(rotation);
|
|
1384
1864
|
const angleDiff = Math.abs(startAngle - endAngle);
|
|
@@ -1404,12 +1884,12 @@ class EllipseCurve extends Curve {
|
|
|
1404
1884
|
}
|
|
1405
1885
|
}
|
|
1406
1886
|
drawTo(ctx) {
|
|
1407
|
-
const { center,
|
|
1887
|
+
const { center, radius, rotation, startAngle, endAngle, clockwise } = this;
|
|
1408
1888
|
ctx.ellipse(
|
|
1409
1889
|
center.x,
|
|
1410
1890
|
center.y,
|
|
1411
|
-
|
|
1412
|
-
|
|
1891
|
+
radius.x,
|
|
1892
|
+
radius.y,
|
|
1413
1893
|
rotation,
|
|
1414
1894
|
startAngle,
|
|
1415
1895
|
endAngle,
|
|
@@ -1429,11 +1909,119 @@ class EllipseCurve extends Curve {
|
|
|
1429
1909
|
}
|
|
1430
1910
|
return this;
|
|
1431
1911
|
}
|
|
1432
|
-
|
|
1912
|
+
getControlPointRefs() {
|
|
1433
1913
|
return [this.center];
|
|
1434
1914
|
}
|
|
1915
|
+
getAdaptivePointArray(output = []) {
|
|
1916
|
+
const i = output.length - 1;
|
|
1917
|
+
const x = this.center.x;
|
|
1918
|
+
const y = this.center.y;
|
|
1919
|
+
const rx = this.radius.x;
|
|
1920
|
+
const ry = this.radius.y;
|
|
1921
|
+
const dx = 0;
|
|
1922
|
+
const dy = 0;
|
|
1923
|
+
if (!(rx >= 0 && ry >= 0 && dx >= 0 && dy >= 0)) {
|
|
1924
|
+
return output;
|
|
1925
|
+
}
|
|
1926
|
+
const n = Math.ceil(2.3 * Math.sqrt(rx + ry));
|
|
1927
|
+
const m = n * 8 + (0) + (0);
|
|
1928
|
+
if (m === 0) {
|
|
1929
|
+
return output;
|
|
1930
|
+
}
|
|
1931
|
+
if (n === 0) {
|
|
1932
|
+
output[i] = output[i + 6] = x + dx;
|
|
1933
|
+
output[i + 1] = output[i + 3] = y + dy;
|
|
1934
|
+
output[i + 2] = output[i + 4] = x - dx;
|
|
1935
|
+
output[i + 5] = output[i + 7] = y - dy;
|
|
1936
|
+
return output;
|
|
1937
|
+
}
|
|
1938
|
+
let j1 = i;
|
|
1939
|
+
let j2 = n * 4 + (0) + 2 + i;
|
|
1940
|
+
let j3 = j2;
|
|
1941
|
+
let j4 = m + i;
|
|
1942
|
+
let x0 = dx + rx;
|
|
1943
|
+
let y0 = dy;
|
|
1944
|
+
let x1 = x + x0;
|
|
1945
|
+
let x2 = x - x0;
|
|
1946
|
+
let y1 = y + y0;
|
|
1947
|
+
output[j1++] = x1;
|
|
1948
|
+
output[j1++] = y1;
|
|
1949
|
+
output[--j2] = y1;
|
|
1950
|
+
output[--j2] = x2;
|
|
1951
|
+
for (let i2 = 1; i2 < n; i2++) {
|
|
1952
|
+
const a = Math.PI / 2 * (i2 / n);
|
|
1953
|
+
const x02 = dx + Math.cos(a) * rx;
|
|
1954
|
+
const y02 = dy + Math.sin(a) * ry;
|
|
1955
|
+
const x12 = x + x02;
|
|
1956
|
+
const x22 = x - x02;
|
|
1957
|
+
const y12 = y + y02;
|
|
1958
|
+
const y22 = y - y02;
|
|
1959
|
+
output[j1++] = x12;
|
|
1960
|
+
output[j1++] = y12;
|
|
1961
|
+
output[--j2] = y12;
|
|
1962
|
+
output[--j2] = x22;
|
|
1963
|
+
output[j3++] = x22;
|
|
1964
|
+
output[j3++] = y22;
|
|
1965
|
+
output[--j4] = y22;
|
|
1966
|
+
output[--j4] = x12;
|
|
1967
|
+
}
|
|
1968
|
+
x0 = dx;
|
|
1969
|
+
y0 = dy + ry;
|
|
1970
|
+
x1 = x + x0;
|
|
1971
|
+
x2 = x - x0;
|
|
1972
|
+
y1 = y + y0;
|
|
1973
|
+
const y2 = y - y0;
|
|
1974
|
+
output[j1++] = x1;
|
|
1975
|
+
output[j1++] = y1;
|
|
1976
|
+
output[--j4] = y2;
|
|
1977
|
+
output[--j4] = x1;
|
|
1978
|
+
return output;
|
|
1979
|
+
}
|
|
1980
|
+
fillTriangulate(options = {}) {
|
|
1981
|
+
let {
|
|
1982
|
+
vertices = [],
|
|
1983
|
+
indices = [],
|
|
1984
|
+
verticesStride = 2,
|
|
1985
|
+
verticesOffset = 0,
|
|
1986
|
+
indicesOffset = 0
|
|
1987
|
+
} = options;
|
|
1988
|
+
const points = this.getAdaptivePointArray();
|
|
1989
|
+
if (points.length === 0) {
|
|
1990
|
+
return { vertices, indices };
|
|
1991
|
+
}
|
|
1992
|
+
let centerX = 0;
|
|
1993
|
+
let centerY = 0;
|
|
1994
|
+
for (let i = 0; i < points.length; i += 2) {
|
|
1995
|
+
centerX += points[i];
|
|
1996
|
+
centerY += points[i + 1];
|
|
1997
|
+
}
|
|
1998
|
+
centerX /= points.length / 2;
|
|
1999
|
+
centerY /= points.length / 2;
|
|
2000
|
+
let count = verticesOffset;
|
|
2001
|
+
vertices[count * verticesStride] = centerX;
|
|
2002
|
+
vertices[count * verticesStride + 1] = centerY;
|
|
2003
|
+
const centerIndex = count++;
|
|
2004
|
+
for (let i = 0; i < points.length; i += 2) {
|
|
2005
|
+
vertices[count * verticesStride] = points[i];
|
|
2006
|
+
vertices[count * verticesStride + 1] = points[i + 1];
|
|
2007
|
+
if (i > 0) {
|
|
2008
|
+
indices[indicesOffset++] = count;
|
|
2009
|
+
indices[indicesOffset++] = centerIndex;
|
|
2010
|
+
indices[indicesOffset++] = count - 1;
|
|
2011
|
+
}
|
|
2012
|
+
count++;
|
|
2013
|
+
}
|
|
2014
|
+
indices[indicesOffset++] = centerIndex + 1;
|
|
2015
|
+
indices[indicesOffset++] = centerIndex;
|
|
2016
|
+
indices[indicesOffset++] = count - 1;
|
|
2017
|
+
return {
|
|
2018
|
+
vertices,
|
|
2019
|
+
indices
|
|
2020
|
+
};
|
|
2021
|
+
}
|
|
1435
2022
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1436
|
-
const { center,
|
|
2023
|
+
const { center, radius, rotation: theta } = this;
|
|
2024
|
+
const { x: rx, y: ry } = radius;
|
|
1437
2025
|
const { x: cx, y: cy } = center;
|
|
1438
2026
|
const cosTheta = Math.cos(theta);
|
|
1439
2027
|
const sinTheta = Math.sin(theta);
|
|
@@ -1453,8 +2041,8 @@ class EllipseCurve extends Curve {
|
|
|
1453
2041
|
super.copy(source);
|
|
1454
2042
|
this.center.x = source.center.x;
|
|
1455
2043
|
this.center.y = source.center.y;
|
|
1456
|
-
this.
|
|
1457
|
-
this.
|
|
2044
|
+
this.radius.x = source.radius.x;
|
|
2045
|
+
this.radius.y = source.radius.y;
|
|
1458
2046
|
this.startAngle = source.startAngle;
|
|
1459
2047
|
this.endAngle = source.endAngle;
|
|
1460
2048
|
this.clockwise = source.clockwise;
|
|
@@ -1463,8 +2051,8 @@ class EllipseCurve extends Curve {
|
|
|
1463
2051
|
}
|
|
1464
2052
|
}
|
|
1465
2053
|
function transfEllipseGeneric(curve, m) {
|
|
1466
|
-
const a = curve.
|
|
1467
|
-
const b = curve.
|
|
2054
|
+
const a = curve.radius.x;
|
|
2055
|
+
const b = curve.radius.y;
|
|
1468
2056
|
const cosTheta = Math.cos(curve.rotation);
|
|
1469
2057
|
const sinTheta = Math.sin(curve.rotation);
|
|
1470
2058
|
const v1 = new Vector2(a * cosTheta, a * sinTheta);
|
|
@@ -1489,8 +2077,8 @@ function transfEllipseGeneric(curve, m) {
|
|
|
1489
2077
|
const ed = eigenDecomposition(mQe[0], mQe[1], mQe[4]);
|
|
1490
2078
|
const rt1sqrt = Math.sqrt(ed.rt1);
|
|
1491
2079
|
const rt2sqrt = Math.sqrt(ed.rt2);
|
|
1492
|
-
curve.
|
|
1493
|
-
curve.
|
|
2080
|
+
curve.radius.x = 1 / rt1sqrt;
|
|
2081
|
+
curve.radius.y = 1 / rt2sqrt;
|
|
1494
2082
|
curve.rotation = Math.atan2(ed.sn, ed.cs);
|
|
1495
2083
|
const isFullEllipse = (curve.endAngle - curve.startAngle) % (2 * Math.PI) < Number.EPSILON;
|
|
1496
2084
|
if (!isFullEllipse) {
|
|
@@ -1531,8 +2119,8 @@ function transfEllipseGeneric(curve, m) {
|
|
|
1531
2119
|
function transfEllipseNoSkew(curve, m) {
|
|
1532
2120
|
const sx = getTransformScaleX(m);
|
|
1533
2121
|
const sy = getTransformScaleY(m);
|
|
1534
|
-
curve.
|
|
1535
|
-
curve.
|
|
2122
|
+
curve.radius.x *= sx;
|
|
2123
|
+
curve.radius.y *= sy;
|
|
1536
2124
|
const theta = sx > Number.EPSILON ? Math.atan2(m.elements[1], m.elements[0]) : Math.atan2(-m.elements[3], m.elements[4]);
|
|
1537
2125
|
curve.rotation += theta;
|
|
1538
2126
|
if (isTransformFlipped(m)) {
|
|
@@ -1594,357 +2182,455 @@ function eigenDecomposition(A, B, C) {
|
|
|
1594
2182
|
cs = 1 / Math.sqrt(1 + t * t);
|
|
1595
2183
|
sn = t * cs;
|
|
1596
2184
|
}
|
|
1597
|
-
if (df > 0) {
|
|
1598
|
-
t = cs;
|
|
1599
|
-
cs = -sn;
|
|
1600
|
-
sn = t;
|
|
2185
|
+
if (df > 0) {
|
|
2186
|
+
t = cs;
|
|
2187
|
+
cs = -sn;
|
|
2188
|
+
sn = t;
|
|
2189
|
+
}
|
|
2190
|
+
return { rt1, rt2, cs, sn };
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
class ArcCurve extends EllipseCurve {
|
|
2194
|
+
constructor(cx = 0, cy = 0, radius = 1, startAngle = 0, endAngle = Math.PI * 2, clockwise = false) {
|
|
2195
|
+
super(cx, cy, radius, radius, 0, startAngle, endAngle, clockwise);
|
|
2196
|
+
}
|
|
2197
|
+
getAdaptivePointArray(output = []) {
|
|
2198
|
+
const start = 0;
|
|
2199
|
+
const end = 1;
|
|
2200
|
+
const x = this.center.x;
|
|
2201
|
+
const y = this.center.y;
|
|
2202
|
+
const clockwise = this.clockwise;
|
|
2203
|
+
const radius = this.radius.x;
|
|
2204
|
+
let dist = Math.abs(start - end);
|
|
2205
|
+
if (clockwise && end > start) {
|
|
2206
|
+
dist = 2 * Math.PI - dist;
|
|
2207
|
+
}
|
|
2208
|
+
let steps = Math.max(6, Math.floor(6 * radius ** (1 / 3) * (dist / Math.PI)));
|
|
2209
|
+
steps = Math.max(steps, 3);
|
|
2210
|
+
let f = dist / steps;
|
|
2211
|
+
let t = start;
|
|
2212
|
+
f *= clockwise ? -1 : 1;
|
|
2213
|
+
for (let i = 0; i < steps + 1; i++) {
|
|
2214
|
+
const cs = Math.cos(t);
|
|
2215
|
+
const sn = Math.sin(t);
|
|
2216
|
+
const nx = x + cs * radius;
|
|
2217
|
+
const ny = y + sn * radius;
|
|
2218
|
+
output.push(nx, ny);
|
|
2219
|
+
t += f;
|
|
2220
|
+
}
|
|
2221
|
+
return output;
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2225
|
+
class CompositeCurve extends Curve {
|
|
2226
|
+
constructor(curves = []) {
|
|
2227
|
+
super();
|
|
2228
|
+
this.curves = curves;
|
|
2229
|
+
}
|
|
2230
|
+
addCurve(curve) {
|
|
2231
|
+
this.curves.push(curve);
|
|
2232
|
+
return this;
|
|
2233
|
+
}
|
|
2234
|
+
getPoint(t, output = new Vector2()) {
|
|
2235
|
+
const d = t * this.getLength();
|
|
2236
|
+
const lengths = this.getLengths();
|
|
2237
|
+
let i = 0;
|
|
2238
|
+
while (i < lengths.length) {
|
|
2239
|
+
if (lengths[i] >= d) {
|
|
2240
|
+
const diff = lengths[i] - d;
|
|
2241
|
+
const curve = this.curves[i];
|
|
2242
|
+
const length = curve.getLength();
|
|
2243
|
+
return curve.getPointAt(
|
|
2244
|
+
length === 0 ? 0 : 1 - diff / length,
|
|
2245
|
+
output
|
|
2246
|
+
);
|
|
2247
|
+
}
|
|
2248
|
+
i++;
|
|
2249
|
+
}
|
|
2250
|
+
return output;
|
|
2251
|
+
}
|
|
2252
|
+
updateLengths() {
|
|
2253
|
+
const arcLengths = [];
|
|
2254
|
+
for (let i = 0, sum = 0, len = this.curves.length; i < len; i++) {
|
|
2255
|
+
sum += this.curves[i].getLength();
|
|
2256
|
+
arcLengths.push(sum);
|
|
2257
|
+
}
|
|
2258
|
+
this._arcLengths = arcLengths;
|
|
2259
|
+
}
|
|
2260
|
+
getControlPointRefs() {
|
|
2261
|
+
return this.curves.flatMap((curve) => curve.getControlPointRefs());
|
|
2262
|
+
}
|
|
2263
|
+
getAdaptivePointArray(output = []) {
|
|
2264
|
+
let offset;
|
|
2265
|
+
this.curves.forEach((curve) => {
|
|
2266
|
+
curve.getAdaptivePointArray(output);
|
|
2267
|
+
if (offset) {
|
|
2268
|
+
if (output[offset - 1] === output[offset + 1] && output[offset] === output[offset + 2]) {
|
|
2269
|
+
output.splice(offset + 1, 2);
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
offset = output.length - 1;
|
|
2273
|
+
});
|
|
2274
|
+
return output;
|
|
2275
|
+
}
|
|
2276
|
+
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
2277
|
+
this.curves.forEach((curve) => curve.getMinMax(min, max));
|
|
2278
|
+
return { min, max };
|
|
2279
|
+
}
|
|
2280
|
+
getBoundingBox() {
|
|
2281
|
+
const { min, max } = this.getMinMax();
|
|
2282
|
+
return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
2283
|
+
}
|
|
2284
|
+
toCommands() {
|
|
2285
|
+
return this.curves.flatMap((curve) => curve.toCommands());
|
|
2286
|
+
}
|
|
2287
|
+
drawTo(ctx) {
|
|
2288
|
+
const point = this.curves[0]?.getPoint(0);
|
|
2289
|
+
if (point) {
|
|
2290
|
+
ctx.moveTo(point.x, point.y);
|
|
2291
|
+
}
|
|
2292
|
+
this.curves.forEach((curve) => curve.drawTo(ctx));
|
|
2293
|
+
return this;
|
|
1601
2294
|
}
|
|
1602
|
-
return { rt1, rt2, cs, sn };
|
|
1603
2295
|
}
|
|
1604
2296
|
|
|
1605
|
-
class
|
|
1606
|
-
|
|
2297
|
+
class CubicBezierCurve extends Curve {
|
|
2298
|
+
p1;
|
|
2299
|
+
cp1;
|
|
2300
|
+
cp2;
|
|
2301
|
+
p2;
|
|
2302
|
+
static from(p1, cp1, cp2, p2) {
|
|
2303
|
+
return new CubicBezierCurve(
|
|
2304
|
+
p1.x,
|
|
2305
|
+
p1.y,
|
|
2306
|
+
cp1.x,
|
|
2307
|
+
cp1.y,
|
|
2308
|
+
cp2.x,
|
|
2309
|
+
cp2.y,
|
|
2310
|
+
p2.x,
|
|
2311
|
+
p2.y
|
|
2312
|
+
);
|
|
2313
|
+
}
|
|
2314
|
+
constructor(p1x, p1y, cp1x, cp1y, cp2x, cp2y, p2x, p2y) {
|
|
1607
2315
|
super();
|
|
1608
|
-
this.
|
|
1609
|
-
this.
|
|
2316
|
+
this.p1 = new Vector2(p1x, p1y);
|
|
2317
|
+
this.cp1 = new Vector2(cp1x, cp1y);
|
|
2318
|
+
this.cp2 = new Vector2(cp2x, cp2y);
|
|
2319
|
+
this.p2 = new Vector2(p2x, p2y);
|
|
1610
2320
|
}
|
|
1611
2321
|
getPoint(t, output = new Vector2()) {
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
return output;
|
|
1618
|
-
}
|
|
1619
|
-
getPointAt(u, output = new Vector2()) {
|
|
1620
|
-
return this.getPoint(u, output);
|
|
2322
|
+
const { p1, cp1, cp2, p2 } = this;
|
|
2323
|
+
return output.set(
|
|
2324
|
+
cubicBezier(t, p1.x, cp1.x, cp2.x, p2.x),
|
|
2325
|
+
cubicBezier(t, p1.y, cp1.y, cp2.y, p2.y)
|
|
2326
|
+
);
|
|
1621
2327
|
}
|
|
1622
|
-
|
|
1623
|
-
return
|
|
2328
|
+
getAdaptivePointArray(output = []) {
|
|
2329
|
+
return getAdaptiveCubicBezierCurvePoints(
|
|
2330
|
+
this.p1.x,
|
|
2331
|
+
this.p1.y,
|
|
2332
|
+
this.cp1.x,
|
|
2333
|
+
this.cp1.y,
|
|
2334
|
+
this.cp2.x,
|
|
2335
|
+
this.cp2.y,
|
|
2336
|
+
this.p2.x,
|
|
2337
|
+
this.p2.y,
|
|
2338
|
+
0.5,
|
|
2339
|
+
output
|
|
2340
|
+
);
|
|
1624
2341
|
}
|
|
1625
|
-
|
|
1626
|
-
return this.
|
|
2342
|
+
getControlPointRefs() {
|
|
2343
|
+
return [this.p1, this.cp1, this.cp2, this.p2];
|
|
1627
2344
|
}
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
2345
|
+
_solveQuadratic(a, b, c) {
|
|
2346
|
+
const discriminant = b * b - 4 * a * c;
|
|
2347
|
+
if (discriminant < 0)
|
|
2348
|
+
return [];
|
|
2349
|
+
const sqrtDiscriminant = Math.sqrt(discriminant);
|
|
2350
|
+
const t1 = (-b + sqrtDiscriminant) / (2 * a);
|
|
2351
|
+
const t2 = (-b - sqrtDiscriminant) / (2 * a);
|
|
2352
|
+
return [t1, t2].filter((t) => t >= 0 && t <= 1);
|
|
1633
2353
|
}
|
|
1634
2354
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1635
|
-
const {
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
2355
|
+
const { p1, cp1, cp2, p2 } = this;
|
|
2356
|
+
const dxRoots = this._solveQuadratic(
|
|
2357
|
+
3 * (cp1.x - p1.x),
|
|
2358
|
+
6 * (cp2.x - cp1.x),
|
|
2359
|
+
3 * (p2.x - cp2.x)
|
|
2360
|
+
);
|
|
2361
|
+
const dyRoots = this._solveQuadratic(
|
|
2362
|
+
3 * (cp1.y - p1.y),
|
|
2363
|
+
6 * (cp2.y - cp1.y),
|
|
2364
|
+
3 * (p2.y - cp2.y)
|
|
2365
|
+
);
|
|
2366
|
+
const tValues = [0, 1, ...dxRoots, ...dyRoots];
|
|
2367
|
+
const samplePoints = (tValues2, precision) => {
|
|
2368
|
+
for (const t of tValues2) {
|
|
2369
|
+
for (let i = 0; i <= precision; i++) {
|
|
2370
|
+
const delta = i / precision - 0.5;
|
|
2371
|
+
const refinedT = Math.min(1, Math.max(0, t + delta));
|
|
2372
|
+
const point = this.getPoint(refinedT);
|
|
2373
|
+
min.x = Math.min(min.x, point.x);
|
|
2374
|
+
min.y = Math.min(min.y, point.y);
|
|
2375
|
+
max.x = Math.max(max.x, point.x);
|
|
2376
|
+
max.y = Math.max(max.y, point.y);
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
};
|
|
2380
|
+
samplePoints(tValues, 10);
|
|
1640
2381
|
return { min, max };
|
|
1641
2382
|
}
|
|
1642
2383
|
toCommands() {
|
|
1643
|
-
const {
|
|
2384
|
+
const { p1, cp1, cp2, p2 } = this;
|
|
1644
2385
|
return [
|
|
1645
|
-
{ type: "M", x:
|
|
1646
|
-
{ type: "
|
|
2386
|
+
{ type: "M", x: p1.x, y: p1.y },
|
|
2387
|
+
{ type: "C", x1: cp1.x, y1: cp1.y, x2: cp2.x, y2: cp2.y, x: p2.x, y: p2.y }
|
|
1647
2388
|
];
|
|
1648
2389
|
}
|
|
1649
2390
|
drawTo(ctx) {
|
|
1650
|
-
const {
|
|
1651
|
-
ctx.lineTo(
|
|
1652
|
-
ctx.
|
|
2391
|
+
const { p1, cp1, cp2, p2 } = this;
|
|
2392
|
+
ctx.lineTo(p1.x, p1.y);
|
|
2393
|
+
ctx.bezierCurveTo(cp1.x, cp1.y, cp2.x, cp2.y, p2.x, p2.y);
|
|
1653
2394
|
return this;
|
|
1654
2395
|
}
|
|
1655
2396
|
copy(source) {
|
|
1656
2397
|
super.copy(source);
|
|
1657
|
-
this.
|
|
1658
|
-
this.
|
|
2398
|
+
this.p1.copy(source.p1);
|
|
2399
|
+
this.cp1.copy(source.cp1);
|
|
2400
|
+
this.cp2.copy(source.cp2);
|
|
2401
|
+
this.p2.copy(source.p2);
|
|
1659
2402
|
return this;
|
|
1660
2403
|
}
|
|
1661
2404
|
}
|
|
1662
2405
|
|
|
1663
|
-
class
|
|
1664
|
-
|
|
2406
|
+
class LineCurve extends Curve {
|
|
2407
|
+
p1;
|
|
2408
|
+
p2;
|
|
2409
|
+
static from(p1, p2) {
|
|
2410
|
+
return new LineCurve(p1.x, p1.y, p2.x, p2.y);
|
|
2411
|
+
}
|
|
2412
|
+
constructor(p1x, p1y, p2x, p2y) {
|
|
1665
2413
|
super();
|
|
1666
|
-
this.
|
|
1667
|
-
this.
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
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;
|
|
2414
|
+
this.p1 = new Vector2(p1x, p1y);
|
|
2415
|
+
this.p2 = new Vector2(p2x, p2y);
|
|
2416
|
+
}
|
|
2417
|
+
getPoint(t, output = new Vector2()) {
|
|
2418
|
+
if (t === 1) {
|
|
2419
|
+
output.copy(this.p2);
|
|
1715
2420
|
} else {
|
|
1716
|
-
|
|
1717
|
-
this.curveT = (val - 5 * Math.PI / 8 - 1.5) / PI_1_2;
|
|
2421
|
+
output.copy(this.p2).sub(this.p1).scale(t).add(this.p1);
|
|
1718
2422
|
}
|
|
1719
|
-
return
|
|
2423
|
+
return output;
|
|
2424
|
+
}
|
|
2425
|
+
getPointAt(u, output = new Vector2()) {
|
|
2426
|
+
return this.getPoint(u, output);
|
|
2427
|
+
}
|
|
2428
|
+
getTangent(_t, output = new Vector2()) {
|
|
2429
|
+
return output.subVectors(this.p2, this.p1).normalize();
|
|
1720
2430
|
}
|
|
1721
|
-
|
|
1722
|
-
return this.
|
|
2431
|
+
getTangentAt(u, output = new Vector2()) {
|
|
2432
|
+
return this.getTangent(u, output);
|
|
1723
2433
|
}
|
|
1724
|
-
|
|
1725
|
-
return this.
|
|
2434
|
+
getControlPointRefs() {
|
|
2435
|
+
return [this.p1, this.p2];
|
|
1726
2436
|
}
|
|
1727
|
-
|
|
1728
|
-
|
|
2437
|
+
getAdaptivePointArray(output = []) {
|
|
2438
|
+
output.push(
|
|
2439
|
+
this.p1.x,
|
|
2440
|
+
this.p1.y,
|
|
2441
|
+
this.p2.x,
|
|
2442
|
+
this.p2.y
|
|
2443
|
+
);
|
|
2444
|
+
return output;
|
|
1729
2445
|
}
|
|
1730
2446
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1731
|
-
|
|
2447
|
+
const { p1, p2 } = this;
|
|
2448
|
+
min.x = Math.min(min.x, p1.x, p2.x);
|
|
2449
|
+
min.y = Math.min(min.y, p1.y, p2.y);
|
|
2450
|
+
max.x = Math.max(max.x, p1.x, p2.x);
|
|
2451
|
+
max.y = Math.max(max.y, p1.y, p2.y);
|
|
1732
2452
|
return { min, max };
|
|
1733
2453
|
}
|
|
1734
2454
|
toCommands() {
|
|
1735
|
-
|
|
2455
|
+
const { p1, p2 } = this;
|
|
2456
|
+
return [
|
|
2457
|
+
{ type: "M", x: p1.x, y: p1.y },
|
|
2458
|
+
{ type: "L", x: p2.x, y: p2.y }
|
|
2459
|
+
];
|
|
1736
2460
|
}
|
|
1737
2461
|
drawTo(ctx) {
|
|
1738
|
-
|
|
2462
|
+
const { p1, p2 } = this;
|
|
2463
|
+
ctx.lineTo(p1.x, p1.y);
|
|
2464
|
+
ctx.lineTo(p2.x, p2.y);
|
|
2465
|
+
return this;
|
|
2466
|
+
}
|
|
2467
|
+
copy(source) {
|
|
2468
|
+
super.copy(source);
|
|
2469
|
+
this.p1.copy(source.p1);
|
|
2470
|
+
this.p2.copy(source.p2);
|
|
1739
2471
|
return this;
|
|
1740
2472
|
}
|
|
1741
2473
|
}
|
|
1742
2474
|
|
|
1743
|
-
class PloygonCurve extends
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
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(
|
|
2475
|
+
class PloygonCurve extends CompositeCurve {
|
|
2476
|
+
static equilateral(cx = 0, cy = 0, radius = 1, sideCount = 3) {
|
|
2477
|
+
const ploygon = new PloygonCurve();
|
|
2478
|
+
const points = [];
|
|
2479
|
+
for (let i = 0; i < sideCount; i++) {
|
|
2480
|
+
const radian = i * 2 * Math.PI / sideCount - 0.5 * Math.PI;
|
|
2481
|
+
points.push(
|
|
1761
2482
|
new Vector2(
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
).add(
|
|
2483
|
+
radius * Math.cos(radian),
|
|
2484
|
+
radius * Math.sin(radian)
|
|
2485
|
+
).add({ x: cx, y: cy })
|
|
1765
2486
|
);
|
|
1766
2487
|
}
|
|
1767
|
-
for (let i = 0; i <
|
|
1768
|
-
|
|
2488
|
+
for (let i = 0; i < sideCount; i++) {
|
|
2489
|
+
ploygon.curves.push(
|
|
2490
|
+
LineCurve.from(points[i], points[(i + 1) % sideCount])
|
|
2491
|
+
);
|
|
1769
2492
|
}
|
|
1770
|
-
return
|
|
1771
|
-
}
|
|
1772
|
-
getCurve(t) {
|
|
1773
|
-
let pos = (t * (this.end - this.start) + this.start) % 1;
|
|
1774
|
-
pos < 0 && (pos += 1);
|
|
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;
|
|
2493
|
+
return ploygon;
|
|
1805
2494
|
}
|
|
1806
2495
|
}
|
|
1807
2496
|
|
|
1808
2497
|
class QuadraticBezierCurve extends Curve {
|
|
1809
|
-
|
|
2498
|
+
p1;
|
|
2499
|
+
cp;
|
|
2500
|
+
p2;
|
|
2501
|
+
static from(p1, cp, p2) {
|
|
2502
|
+
return new QuadraticBezierCurve(
|
|
2503
|
+
p1.x,
|
|
2504
|
+
p1.y,
|
|
2505
|
+
cp.x,
|
|
2506
|
+
cp.y,
|
|
2507
|
+
p2.x,
|
|
2508
|
+
p2.y
|
|
2509
|
+
);
|
|
2510
|
+
}
|
|
2511
|
+
constructor(p1x, p1y, cpx, cpy, p2x, p2y) {
|
|
1810
2512
|
super();
|
|
1811
|
-
this.
|
|
1812
|
-
this.
|
|
1813
|
-
this.
|
|
2513
|
+
this.p1 = new Vector2(p1x, p1y);
|
|
2514
|
+
this.cp = new Vector2(cpx, cpy);
|
|
2515
|
+
this.p2 = new Vector2(p2x, p2y);
|
|
1814
2516
|
}
|
|
1815
2517
|
getPoint(t, output = new Vector2()) {
|
|
1816
|
-
const {
|
|
2518
|
+
const { p1, cp, p2 } = this;
|
|
1817
2519
|
output.set(
|
|
1818
|
-
quadraticBezier(t,
|
|
1819
|
-
quadraticBezier(t,
|
|
2520
|
+
quadraticBezier(t, p1.x, cp.x, p2.x),
|
|
2521
|
+
quadraticBezier(t, p1.y, cp.y, p2.y)
|
|
1820
2522
|
);
|
|
1821
2523
|
return output;
|
|
1822
2524
|
}
|
|
1823
|
-
|
|
1824
|
-
return [
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
2525
|
+
getControlPointRefs() {
|
|
2526
|
+
return [this.p1, this.cp, this.p2];
|
|
2527
|
+
}
|
|
2528
|
+
getAdaptivePointArray(output = []) {
|
|
2529
|
+
return getAdaptiveQuadraticBezierCurvePoints(
|
|
2530
|
+
this.p1.x,
|
|
2531
|
+
this.p1.y,
|
|
2532
|
+
this.cp.x,
|
|
2533
|
+
this.cp.y,
|
|
2534
|
+
this.p2.x,
|
|
2535
|
+
this.p2.y,
|
|
2536
|
+
0.5,
|
|
2537
|
+
output
|
|
2538
|
+
);
|
|
1829
2539
|
}
|
|
1830
2540
|
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,
|
|
2541
|
+
const { p1, cp, p2 } = this;
|
|
2542
|
+
const x1 = 0.5 * (p1.x + cp.x);
|
|
2543
|
+
const y1 = 0.5 * (p1.y + cp.y);
|
|
2544
|
+
const x2 = 0.5 * (p1.x + p2.x);
|
|
2545
|
+
const y2 = 0.5 * (p1.y + p2.y);
|
|
2546
|
+
min.x = Math.min(min.x, p1.x, p2.x, x1, x2);
|
|
2547
|
+
min.y = Math.min(min.y, p1.y, p2.y, y1, y2);
|
|
2548
|
+
max.x = Math.max(max.x, p1.x, p2.x, x1, x2);
|
|
2549
|
+
max.y = Math.max(max.y, p1.y, p2.y, y1, y2);
|
|
1840
2550
|
return { min, max };
|
|
1841
2551
|
}
|
|
1842
2552
|
toCommands() {
|
|
1843
|
-
const {
|
|
2553
|
+
const { p1, cp, p2 } = this;
|
|
1844
2554
|
return [
|
|
1845
|
-
{ type: "M", x:
|
|
1846
|
-
{ type: "Q", x1:
|
|
2555
|
+
{ type: "M", x: p1.x, y: p1.y },
|
|
2556
|
+
{ type: "Q", x1: cp.x, y1: cp.y, x: p2.x, y: p2.y }
|
|
1847
2557
|
];
|
|
1848
2558
|
}
|
|
1849
2559
|
drawTo(ctx) {
|
|
1850
|
-
const {
|
|
1851
|
-
ctx.lineTo(
|
|
1852
|
-
ctx.quadraticCurveTo(
|
|
2560
|
+
const { p1, cp, p2 } = this;
|
|
2561
|
+
ctx.lineTo(p1.x, p1.y);
|
|
2562
|
+
ctx.quadraticCurveTo(cp.x, cp.y, p2.x, p2.y);
|
|
1853
2563
|
return this;
|
|
1854
2564
|
}
|
|
1855
2565
|
copy(source) {
|
|
1856
2566
|
super.copy(source);
|
|
1857
|
-
this.
|
|
1858
|
-
this.
|
|
1859
|
-
this.
|
|
2567
|
+
this.p1.copy(source.p1);
|
|
2568
|
+
this.cp.copy(source.cp);
|
|
2569
|
+
this.p2.copy(source.p2);
|
|
1860
2570
|
return this;
|
|
1861
2571
|
}
|
|
1862
2572
|
}
|
|
1863
2573
|
|
|
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;
|
|
2574
|
+
class RectangleCurve extends PloygonCurve {
|
|
2575
|
+
constructor(x = 0, y = 0, width = 0, height = 0) {
|
|
1892
2576
|
const points = [
|
|
1893
|
-
new Vector2(x
|
|
1894
|
-
new Vector2(x +
|
|
1895
|
-
new Vector2(x +
|
|
1896
|
-
new Vector2(x
|
|
2577
|
+
new Vector2(x, y),
|
|
2578
|
+
new Vector2(x + width, y),
|
|
2579
|
+
new Vector2(x + width, y + height),
|
|
2580
|
+
new Vector2(x, y + height)
|
|
1897
2581
|
];
|
|
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());
|
|
1937
|
-
}
|
|
1938
|
-
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1939
|
-
this.curves.forEach((curve) => curve.getMinMax(min, max));
|
|
1940
|
-
return { min, max };
|
|
1941
|
-
}
|
|
1942
|
-
toCommands() {
|
|
1943
|
-
return this.curves.flatMap((curve) => curve.toCommands());
|
|
2582
|
+
super([
|
|
2583
|
+
LineCurve.from(points[0], points[1]),
|
|
2584
|
+
LineCurve.from(points[1], points[2]),
|
|
2585
|
+
LineCurve.from(points[2], points[3]),
|
|
2586
|
+
LineCurve.from(points[3], points[0])
|
|
2587
|
+
]);
|
|
2588
|
+
this.x = x;
|
|
2589
|
+
this.y = y;
|
|
2590
|
+
this.width = width;
|
|
2591
|
+
this.height = height;
|
|
1944
2592
|
}
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
2593
|
+
fillTriangulate(options = {}) {
|
|
2594
|
+
let {
|
|
2595
|
+
vertices = [],
|
|
2596
|
+
indices = [],
|
|
2597
|
+
verticesStride = 2,
|
|
2598
|
+
verticesOffset = 0,
|
|
2599
|
+
indicesOffset = 0
|
|
2600
|
+
} = options;
|
|
2601
|
+
const { x, y, width, height } = this;
|
|
2602
|
+
const points = [
|
|
2603
|
+
x,
|
|
2604
|
+
y,
|
|
2605
|
+
x + width,
|
|
2606
|
+
y,
|
|
2607
|
+
x + width,
|
|
2608
|
+
y + height,
|
|
2609
|
+
x,
|
|
2610
|
+
y + height
|
|
2611
|
+
];
|
|
2612
|
+
let count = 0;
|
|
2613
|
+
verticesOffset *= verticesStride;
|
|
2614
|
+
vertices[verticesOffset + count] = points[0];
|
|
2615
|
+
vertices[verticesOffset + count + 1] = points[1];
|
|
2616
|
+
count += verticesStride;
|
|
2617
|
+
vertices[verticesOffset + count] = points[2];
|
|
2618
|
+
vertices[verticesOffset + count + 1] = points[3];
|
|
2619
|
+
count += verticesStride;
|
|
2620
|
+
vertices[verticesOffset + count] = points[6];
|
|
2621
|
+
vertices[verticesOffset + count + 1] = points[7];
|
|
2622
|
+
count += verticesStride;
|
|
2623
|
+
vertices[verticesOffset + count] = points[4];
|
|
2624
|
+
vertices[verticesOffset + count + 1] = points[5];
|
|
2625
|
+
count += verticesStride;
|
|
2626
|
+
const verticesIndex = verticesOffset / verticesStride;
|
|
2627
|
+
indices[indicesOffset++] = verticesIndex;
|
|
2628
|
+
indices[indicesOffset++] = verticesIndex + 1;
|
|
2629
|
+
indices[indicesOffset++] = verticesIndex + 2;
|
|
2630
|
+
indices[indicesOffset++] = verticesIndex + 1;
|
|
2631
|
+
indices[indicesOffset++] = verticesIndex + 3;
|
|
2632
|
+
indices[indicesOffset++] = verticesIndex + 2;
|
|
2633
|
+
return { vertices, indices };
|
|
1948
2634
|
}
|
|
1949
2635
|
}
|
|
1950
2636
|
|
|
@@ -1968,7 +2654,7 @@ class SplineCurve extends Curve {
|
|
|
1968
2654
|
);
|
|
1969
2655
|
return output;
|
|
1970
2656
|
}
|
|
1971
|
-
|
|
2657
|
+
getControlPointRefs() {
|
|
1972
2658
|
return this.points;
|
|
1973
2659
|
}
|
|
1974
2660
|
copy(source) {
|
|
@@ -1981,22 +2667,16 @@ class SplineCurve extends Curve {
|
|
|
1981
2667
|
}
|
|
1982
2668
|
}
|
|
1983
2669
|
|
|
1984
|
-
class CurvePath extends
|
|
1985
|
-
curves = [];
|
|
2670
|
+
class CurvePath extends CompositeCurve {
|
|
1986
2671
|
startPoint;
|
|
1987
2672
|
currentPoint;
|
|
1988
2673
|
autoClose = false;
|
|
1989
|
-
_cacheLengths = [];
|
|
1990
2674
|
constructor(points) {
|
|
1991
2675
|
super();
|
|
1992
2676
|
if (points) {
|
|
1993
2677
|
this.addPoints(points);
|
|
1994
2678
|
}
|
|
1995
2679
|
}
|
|
1996
|
-
addCurve(curve) {
|
|
1997
|
-
this.curves.push(curve);
|
|
1998
|
-
return this;
|
|
1999
|
-
}
|
|
2000
2680
|
addPoints(points) {
|
|
2001
2681
|
this.moveTo(points[0].x, points[0].y);
|
|
2002
2682
|
for (let i = 1, len = points.length; i < len; i++) {
|
|
@@ -2006,82 +2686,26 @@ class CurvePath extends Curve {
|
|
|
2006
2686
|
return this;
|
|
2007
2687
|
}
|
|
2008
2688
|
addCommands(commands) {
|
|
2009
|
-
|
|
2689
|
+
svgPathCommandsAddToPath2D(commands, this);
|
|
2010
2690
|
return this;
|
|
2011
2691
|
}
|
|
2012
2692
|
addData(data) {
|
|
2013
|
-
this.addCommands(
|
|
2693
|
+
this.addCommands(svgPathDataToCommands(data));
|
|
2014
2694
|
return this;
|
|
2015
2695
|
}
|
|
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++;
|
|
2696
|
+
getUnevenPointArray(count = 40, output = []) {
|
|
2697
|
+
super.getUnevenPointArray(count, output);
|
|
2698
|
+
if (this.autoClose && output.length >= 4 && (output[0] !== output[output.length - 2] && output[1] !== output[output.length - 1])) {
|
|
2699
|
+
output.push(output[0], output[1]);
|
|
2028
2700
|
}
|
|
2029
2701
|
return output;
|
|
2030
2702
|
}
|
|
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
|
-
}
|
|
2703
|
+
getSpacedPointArray(count = 40, output = []) {
|
|
2704
|
+
super.getSpacedPointArray(count, output);
|
|
2705
|
+
if (this.autoClose && output.length >= 4 && (output[0] !== output[output.length - 2] && output[1] !== output[output.length - 1])) {
|
|
2706
|
+
output.push(output[0], output[1]);
|
|
2080
2707
|
}
|
|
2081
|
-
|
|
2082
|
-
points.push(points[0]);
|
|
2083
|
-
}
|
|
2084
|
-
return points;
|
|
2708
|
+
return output;
|
|
2085
2709
|
}
|
|
2086
2710
|
_setCurrentPoint(point) {
|
|
2087
2711
|
this.currentPoint = new Vector2(point.x, point.y);
|
|
@@ -2090,12 +2714,21 @@ class CurvePath extends Curve {
|
|
|
2090
2714
|
}
|
|
2091
2715
|
return this;
|
|
2092
2716
|
}
|
|
2717
|
+
_connetLineTo(curve) {
|
|
2718
|
+
if (this.curves.length > 0) {
|
|
2719
|
+
const first = curve.getPoint(0);
|
|
2720
|
+
if (!this.currentPoint || !first.equals(this.currentPoint)) {
|
|
2721
|
+
this.lineTo(first.x, first.y);
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
return this;
|
|
2725
|
+
}
|
|
2093
2726
|
closePath() {
|
|
2094
2727
|
const start = this.startPoint;
|
|
2095
2728
|
if (start) {
|
|
2096
2729
|
const end = this.currentPoint;
|
|
2097
2730
|
if (end && !start.equals(end)) {
|
|
2098
|
-
this.curves.push(
|
|
2731
|
+
this.curves.push(LineCurve.from(end, start));
|
|
2099
2732
|
end.copy(start);
|
|
2100
2733
|
}
|
|
2101
2734
|
this.startPoint = void 0;
|
|
@@ -2112,8 +2745,10 @@ class CurvePath extends Curve {
|
|
|
2112
2745
|
if (!start?.equals({ x, y })) {
|
|
2113
2746
|
this.curves.push(
|
|
2114
2747
|
new LineCurve(
|
|
2115
|
-
start?.
|
|
2116
|
-
|
|
2748
|
+
start?.x ?? 0,
|
|
2749
|
+
start?.y ?? 0,
|
|
2750
|
+
x,
|
|
2751
|
+
y
|
|
2117
2752
|
)
|
|
2118
2753
|
);
|
|
2119
2754
|
}
|
|
@@ -2125,10 +2760,14 @@ class CurvePath extends Curve {
|
|
|
2125
2760
|
if (!start?.equals({ x, y })) {
|
|
2126
2761
|
this.curves.push(
|
|
2127
2762
|
new CubicBezierCurve(
|
|
2128
|
-
start?.
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2763
|
+
start?.x ?? 0,
|
|
2764
|
+
start?.y ?? 0,
|
|
2765
|
+
cp1x,
|
|
2766
|
+
cp1y,
|
|
2767
|
+
cp2x,
|
|
2768
|
+
cp2y,
|
|
2769
|
+
x,
|
|
2770
|
+
y
|
|
2132
2771
|
)
|
|
2133
2772
|
);
|
|
2134
2773
|
}
|
|
@@ -2140,9 +2779,12 @@ class CurvePath extends Curve {
|
|
|
2140
2779
|
if (!start?.equals({ x, y })) {
|
|
2141
2780
|
this.curves.push(
|
|
2142
2781
|
new QuadraticBezierCurve(
|
|
2143
|
-
start?.
|
|
2144
|
-
|
|
2145
|
-
|
|
2782
|
+
start?.x ?? 0,
|
|
2783
|
+
start?.y ?? 0,
|
|
2784
|
+
cpx,
|
|
2785
|
+
cpy,
|
|
2786
|
+
x,
|
|
2787
|
+
y
|
|
2146
2788
|
)
|
|
2147
2789
|
);
|
|
2148
2790
|
}
|
|
@@ -2150,12 +2792,23 @@ class CurvePath extends Curve {
|
|
|
2150
2792
|
return this;
|
|
2151
2793
|
}
|
|
2152
2794
|
arc(x, y, radius, startAngle, endAngle, counterclockwise) {
|
|
2153
|
-
|
|
2795
|
+
const curve = new ArcCurve(
|
|
2796
|
+
x,
|
|
2797
|
+
y,
|
|
2798
|
+
radius,
|
|
2799
|
+
startAngle,
|
|
2800
|
+
endAngle,
|
|
2801
|
+
!counterclockwise
|
|
2802
|
+
);
|
|
2803
|
+
this._connetLineTo(curve);
|
|
2804
|
+
this.curves.push(curve);
|
|
2805
|
+
this._setCurrentPoint(curve.getPoint(1));
|
|
2154
2806
|
return this;
|
|
2155
2807
|
}
|
|
2156
2808
|
relativeArc(x, y, radius, startAngle, endAngle, counterclockwise) {
|
|
2157
|
-
|
|
2158
|
-
|
|
2809
|
+
x += this.currentPoint?.x ?? 0;
|
|
2810
|
+
y += this.currentPoint?.y ?? 0;
|
|
2811
|
+
this.arc(x, y, radius, startAngle, endAngle, counterclockwise);
|
|
2159
2812
|
return this;
|
|
2160
2813
|
}
|
|
2161
2814
|
// TODO
|
|
@@ -2166,7 +2819,8 @@ class CurvePath extends Curve {
|
|
|
2166
2819
|
}
|
|
2167
2820
|
ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise = true) {
|
|
2168
2821
|
const curve = new EllipseCurve(
|
|
2169
|
-
|
|
2822
|
+
x,
|
|
2823
|
+
y,
|
|
2170
2824
|
radiusX,
|
|
2171
2825
|
radiusY,
|
|
2172
2826
|
rotation,
|
|
@@ -2174,29 +2828,21 @@ class CurvePath extends Curve {
|
|
|
2174
2828
|
endAngle,
|
|
2175
2829
|
!counterclockwise
|
|
2176
2830
|
);
|
|
2177
|
-
|
|
2178
|
-
const first = curve.getPoint(0);
|
|
2179
|
-
if (!this.currentPoint || !first.equals(this.currentPoint)) {
|
|
2180
|
-
this.lineTo(first.x, first.y);
|
|
2181
|
-
}
|
|
2182
|
-
}
|
|
2831
|
+
this._connetLineTo(curve);
|
|
2183
2832
|
this.curves.push(curve);
|
|
2184
2833
|
this._setCurrentPoint(curve.getPoint(1));
|
|
2185
2834
|
return this;
|
|
2186
2835
|
}
|
|
2187
2836
|
relativeEllipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) {
|
|
2188
|
-
|
|
2189
|
-
|
|
2837
|
+
x += this.currentPoint?.x ?? 0;
|
|
2838
|
+
y += this.currentPoint?.y ?? 0;
|
|
2839
|
+
this.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise);
|
|
2190
2840
|
return this;
|
|
2191
2841
|
}
|
|
2192
2842
|
rect(x, y, w, h) {
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
w / 2,
|
|
2197
|
-
w / h
|
|
2198
|
-
)
|
|
2199
|
-
);
|
|
2843
|
+
const curve = new RectangleCurve(x, y, w, h);
|
|
2844
|
+
this._connetLineTo(curve);
|
|
2845
|
+
this.curves.push(curve);
|
|
2200
2846
|
this._setCurrentPoint({ x, y });
|
|
2201
2847
|
return this;
|
|
2202
2848
|
}
|
|
@@ -2206,17 +2852,6 @@ class CurvePath extends Curve {
|
|
|
2206
2852
|
this._setCurrentPoint(points[points.length - 1]);
|
|
2207
2853
|
return this;
|
|
2208
2854
|
}
|
|
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
2855
|
drawTo(ctx) {
|
|
2221
2856
|
const point = this.curves[0]?.getPoint(0);
|
|
2222
2857
|
if (point) {
|
|
@@ -2267,20 +2902,22 @@ function getIntersectionPoint(p1, p2, q1, q2) {
|
|
|
2267
2902
|
);
|
|
2268
2903
|
}
|
|
2269
2904
|
|
|
2270
|
-
class Path2D {
|
|
2271
|
-
|
|
2272
|
-
paths = [this.currentPath];
|
|
2905
|
+
class Path2D extends CompositeCurve {
|
|
2906
|
+
currentCurve = new CurvePath();
|
|
2273
2907
|
style;
|
|
2274
2908
|
get startPoint() {
|
|
2275
|
-
return this.
|
|
2909
|
+
return this.currentCurve.startPoint;
|
|
2276
2910
|
}
|
|
2277
2911
|
get currentPoint() {
|
|
2278
|
-
return this.
|
|
2912
|
+
return this.currentCurve.currentPoint;
|
|
2279
2913
|
}
|
|
2280
2914
|
get strokeWidth() {
|
|
2281
2915
|
return this.style.strokeWidth ?? ((this.style.stroke ?? "none") === "none" ? 0 : 1);
|
|
2282
2916
|
}
|
|
2283
2917
|
constructor(path, style = {}) {
|
|
2918
|
+
super();
|
|
2919
|
+
this.curves.push(this.currentCurve);
|
|
2920
|
+
this.style = style;
|
|
2284
2921
|
if (path) {
|
|
2285
2922
|
if (path instanceof Path2D) {
|
|
2286
2923
|
this.addPath(path);
|
|
@@ -2290,99 +2927,90 @@ class Path2D {
|
|
|
2290
2927
|
this.addData(path);
|
|
2291
2928
|
}
|
|
2292
2929
|
}
|
|
2293
|
-
this.style = style;
|
|
2294
2930
|
}
|
|
2295
2931
|
addPath(path) {
|
|
2296
2932
|
if (path instanceof Path2D) {
|
|
2297
|
-
this.
|
|
2933
|
+
this.curves.push(...path.curves.map((v) => v.clone()));
|
|
2298
2934
|
} else {
|
|
2299
|
-
this.
|
|
2935
|
+
this.curves.push(path);
|
|
2300
2936
|
}
|
|
2301
2937
|
return this;
|
|
2302
2938
|
}
|
|
2303
2939
|
closePath() {
|
|
2304
2940
|
const startPoint = this.startPoint;
|
|
2305
2941
|
if (startPoint) {
|
|
2306
|
-
this.
|
|
2307
|
-
if (this.
|
|
2308
|
-
this.
|
|
2309
|
-
this.
|
|
2942
|
+
this.currentCurve.closePath();
|
|
2943
|
+
if (this.currentCurve.curves.length > 0) {
|
|
2944
|
+
this.currentCurve = new CurvePath().moveTo(startPoint.x, startPoint.y);
|
|
2945
|
+
this.curves.push(this.currentCurve);
|
|
2310
2946
|
}
|
|
2311
2947
|
}
|
|
2312
2948
|
return this;
|
|
2313
2949
|
}
|
|
2314
2950
|
moveTo(x, y) {
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
this.
|
|
2319
|
-
this.paths.push(this.currentPath);
|
|
2320
|
-
} else {
|
|
2321
|
-
this.currentPath.moveTo(x, y);
|
|
2951
|
+
if (!this.currentCurve.currentPoint?.equals({ x, y })) {
|
|
2952
|
+
if (this.currentCurve.curves.length) {
|
|
2953
|
+
this.currentCurve = new CurvePath();
|
|
2954
|
+
this.curves.push(this.currentCurve);
|
|
2322
2955
|
}
|
|
2956
|
+
this.currentCurve.moveTo(x, y);
|
|
2323
2957
|
}
|
|
2324
2958
|
return this;
|
|
2325
2959
|
}
|
|
2326
2960
|
lineTo(x, y) {
|
|
2327
|
-
this.
|
|
2961
|
+
this.currentCurve.lineTo(x, y);
|
|
2328
2962
|
return this;
|
|
2329
2963
|
}
|
|
2330
2964
|
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
|
|
2331
|
-
this.
|
|
2965
|
+
this.currentCurve.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
|
|
2332
2966
|
return this;
|
|
2333
2967
|
}
|
|
2334
2968
|
quadraticCurveTo(cpx, cpy, x, y) {
|
|
2335
|
-
this.
|
|
2969
|
+
this.currentCurve.quadraticCurveTo(cpx, cpy, x, y);
|
|
2336
2970
|
return this;
|
|
2337
2971
|
}
|
|
2338
2972
|
arc(x, y, radius, startAngle, endAngle, counterclockwise) {
|
|
2339
|
-
this.
|
|
2973
|
+
this.currentCurve.arc(x, y, radius, startAngle, endAngle, counterclockwise);
|
|
2340
2974
|
return this;
|
|
2341
2975
|
}
|
|
2342
2976
|
arcTo(x1, y1, x2, y2, radius) {
|
|
2343
|
-
this.
|
|
2977
|
+
this.currentCurve.arcTo(x1, y1, x2, y2, radius);
|
|
2344
2978
|
return this;
|
|
2345
2979
|
}
|
|
2346
2980
|
ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) {
|
|
2347
|
-
this.
|
|
2981
|
+
this.currentCurve.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise);
|
|
2348
2982
|
return this;
|
|
2349
2983
|
}
|
|
2350
2984
|
rect(x, y, w, h) {
|
|
2351
|
-
this.
|
|
2985
|
+
this.currentCurve.rect(x, y, w, h);
|
|
2352
2986
|
return this;
|
|
2353
2987
|
}
|
|
2354
2988
|
addCommands(commands) {
|
|
2355
|
-
|
|
2989
|
+
svgPathCommandsAddToPath2D(commands, this);
|
|
2356
2990
|
return this;
|
|
2357
2991
|
}
|
|
2358
2992
|
addData(data) {
|
|
2359
|
-
this.addCommands(
|
|
2993
|
+
this.addCommands(svgPathDataToCommands(data));
|
|
2360
2994
|
return this;
|
|
2361
2995
|
}
|
|
2362
2996
|
splineThru(points) {
|
|
2363
|
-
this.
|
|
2997
|
+
this.currentCurve.splineThru(points);
|
|
2364
2998
|
return this;
|
|
2365
2999
|
}
|
|
2366
|
-
getControlPoints() {
|
|
2367
|
-
return this.paths.flatMap((path) => path.getControlPoints());
|
|
2368
|
-
}
|
|
2369
|
-
getCurves() {
|
|
2370
|
-
return this.paths.flatMap((path) => path.curves);
|
|
2371
|
-
}
|
|
2372
3000
|
scale(sx, sy = sx, target = { x: 0, y: 0 }) {
|
|
2373
|
-
this.
|
|
3001
|
+
this.getControlPointRefs().forEach((point) => {
|
|
2374
3002
|
point.scale(sx, sy, target);
|
|
2375
3003
|
});
|
|
2376
3004
|
return this;
|
|
2377
3005
|
}
|
|
2378
3006
|
skew(ax, ay = 0, target = { x: 0, y: 0 }) {
|
|
2379
|
-
this.
|
|
3007
|
+
this.getControlPointRefs().forEach((point) => {
|
|
2380
3008
|
point.skew(ax, ay, target);
|
|
2381
3009
|
});
|
|
2382
3010
|
return this;
|
|
2383
3011
|
}
|
|
2384
3012
|
rotate(a, target = { x: 0, y: 0 }) {
|
|
2385
|
-
this.
|
|
3013
|
+
this.getControlPointRefs().forEach((point) => {
|
|
2386
3014
|
point.rotate(a, target);
|
|
2387
3015
|
});
|
|
2388
3016
|
return this;
|
|
@@ -2391,12 +3019,14 @@ class Path2D {
|
|
|
2391
3019
|
if (b === 0) {
|
|
2392
3020
|
return this;
|
|
2393
3021
|
}
|
|
2394
|
-
const curves = this.
|
|
3022
|
+
const curves = this.curves;
|
|
2395
3023
|
const _list = [];
|
|
2396
3024
|
const _isClockwise = [];
|
|
2397
3025
|
const _points = [];
|
|
2398
3026
|
curves.forEach((curve, index) => {
|
|
2399
|
-
|
|
3027
|
+
if (!curve.getLength())
|
|
3028
|
+
return;
|
|
3029
|
+
const points = curve.getControlPointRefs();
|
|
2400
3030
|
const isClockwise = curve.isClockwise();
|
|
2401
3031
|
_points[index] = points;
|
|
2402
3032
|
_isClockwise[index] = isClockwise;
|
|
@@ -2418,6 +3048,8 @@ class Path2D {
|
|
|
2418
3048
|
});
|
|
2419
3049
|
});
|
|
2420
3050
|
curves.forEach((curve, index) => {
|
|
3051
|
+
if (!curve.getLength())
|
|
3052
|
+
return;
|
|
2421
3053
|
const isClockwise = _isClockwise[index];
|
|
2422
3054
|
const points = _points[index];
|
|
2423
3055
|
points.forEach((point) => {
|
|
@@ -2445,19 +3077,19 @@ class Path2D {
|
|
|
2445
3077
|
return this;
|
|
2446
3078
|
}
|
|
2447
3079
|
matrix(matrix) {
|
|
2448
|
-
this.
|
|
3080
|
+
this.curves.forEach((curve) => curve.matrix(matrix));
|
|
2449
3081
|
return this;
|
|
2450
3082
|
}
|
|
2451
3083
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN, withStyle = true) {
|
|
2452
3084
|
const strokeWidth = this.strokeWidth;
|
|
2453
|
-
this.
|
|
3085
|
+
this.curves.forEach((curve) => {
|
|
2454
3086
|
curve.getMinMax(min, max);
|
|
2455
3087
|
if (withStyle) {
|
|
2456
3088
|
if (strokeWidth > 1) {
|
|
2457
3089
|
const halfStrokeWidth = strokeWidth / 2;
|
|
2458
3090
|
const isClockwise = curve.isClockwise();
|
|
2459
3091
|
const points = [];
|
|
2460
|
-
for (let t = 0; t <= 1; t += 1 / curve.
|
|
3092
|
+
for (let t = 0; t <= 1; t += 1 / curve.arcLengthDivision) {
|
|
2461
3093
|
const point = curve.getPoint(t);
|
|
2462
3094
|
const normal = curve.getNormal(t);
|
|
2463
3095
|
const dist1 = normal.clone().scale(isClockwise ? halfStrokeWidth : -halfStrokeWidth);
|
|
@@ -2490,7 +3122,7 @@ class Path2D {
|
|
|
2490
3122
|
ctx.beginPath();
|
|
2491
3123
|
ctx.save();
|
|
2492
3124
|
setCanvasContext(ctx, style);
|
|
2493
|
-
this.
|
|
3125
|
+
this.curves.forEach((path) => {
|
|
2494
3126
|
path.drawTo(ctx);
|
|
2495
3127
|
});
|
|
2496
3128
|
if (fill !== "none") {
|
|
@@ -2508,9 +3140,8 @@ class Path2D {
|
|
|
2508
3140
|
ctx.beginPath();
|
|
2509
3141
|
ctx.save();
|
|
2510
3142
|
setCanvasContext(ctx, style);
|
|
2511
|
-
this.
|
|
2512
|
-
ctx
|
|
2513
|
-
ctx.arc(point.x, point.y, 4, 0, Math.PI * 2);
|
|
3143
|
+
this.getControlPointRefs().forEach((point) => {
|
|
3144
|
+
drawPoint(ctx, point.x, point.y, { radius: 4 });
|
|
2514
3145
|
});
|
|
2515
3146
|
if (fill !== "none") {
|
|
2516
3147
|
ctx.fill();
|
|
@@ -2522,10 +3153,10 @@ class Path2D {
|
|
|
2522
3153
|
return this;
|
|
2523
3154
|
}
|
|
2524
3155
|
toCommands() {
|
|
2525
|
-
return this.
|
|
3156
|
+
return this.curves.flatMap((v) => v.toCommands());
|
|
2526
3157
|
}
|
|
2527
3158
|
toData() {
|
|
2528
|
-
return this.
|
|
3159
|
+
return this.curves.filter((v) => v.curves.length).map((v) => v.toData()).join(" ");
|
|
2529
3160
|
}
|
|
2530
3161
|
toSVGPathString() {
|
|
2531
3162
|
const style = {
|
|
@@ -2550,10 +3181,34 @@ class Path2D {
|
|
|
2550
3181
|
}
|
|
2551
3182
|
return `<path d="${this.toData()}" style="${cssText}"></path>`;
|
|
2552
3183
|
}
|
|
3184
|
+
copy(source) {
|
|
3185
|
+
this.currentCurve = source.currentCurve.clone();
|
|
3186
|
+
this.curves = source.curves.map((path) => path.clone());
|
|
3187
|
+
this.style = { ...source.style };
|
|
3188
|
+
return this;
|
|
3189
|
+
}
|
|
3190
|
+
clone() {
|
|
3191
|
+
return new this.constructor().copy(this);
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
|
|
3195
|
+
class Path2DSet {
|
|
3196
|
+
constructor(paths = []) {
|
|
3197
|
+
this.paths = paths;
|
|
3198
|
+
}
|
|
3199
|
+
getBoundingBox(withStyle = true) {
|
|
3200
|
+
if (!this.paths.length) {
|
|
3201
|
+
return void 0;
|
|
3202
|
+
}
|
|
3203
|
+
const min = Vector2.MAX;
|
|
3204
|
+
const max = Vector2.MIN;
|
|
3205
|
+
this.paths.forEach((path) => path.getMinMax(min, max, withStyle));
|
|
3206
|
+
return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
3207
|
+
}
|
|
2553
3208
|
toSVGString() {
|
|
2554
3209
|
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">${
|
|
3210
|
+
const content = this.paths.map((path) => path.toSVGPathString()).join("");
|
|
3211
|
+
return `<svg viewBox="${x} ${y} ${width} ${height}" width="${width}px" height="${height}px" xmlns="http://www.w3.org/2000/svg">${content}</svg>`;
|
|
2557
3212
|
}
|
|
2558
3213
|
toSVGUrl() {
|
|
2559
3214
|
return `data:image/svg+xml;base64,${btoa(this.toSVGString())}`;
|
|
@@ -2573,19 +3228,12 @@ class Path2D {
|
|
|
2573
3228
|
if (ctx) {
|
|
2574
3229
|
ctx.scale(pixelRatio, pixelRatio);
|
|
2575
3230
|
ctx.translate(-left, -top);
|
|
2576
|
-
this.
|
|
3231
|
+
this.paths.forEach((path) => {
|
|
3232
|
+
path.drawTo(ctx, style);
|
|
3233
|
+
});
|
|
2577
3234
|
}
|
|
2578
3235
|
return canvas;
|
|
2579
3236
|
}
|
|
2580
|
-
copy(source) {
|
|
2581
|
-
this.currentPath = source.currentPath.clone();
|
|
2582
|
-
this.paths = source.paths.map((path) => path.clone());
|
|
2583
|
-
this.style = { ...source.style };
|
|
2584
|
-
return this;
|
|
2585
|
-
}
|
|
2586
|
-
clone() {
|
|
2587
|
-
return new this.constructor().copy(this);
|
|
2588
|
-
}
|
|
2589
3237
|
}
|
|
2590
3238
|
|
|
2591
3239
|
const defaultUnit = "px";
|
|
@@ -3043,7 +3691,7 @@ function parseNode(node, style, paths = [], stylesheets = {}) {
|
|
|
3043
3691
|
break;
|
|
3044
3692
|
case "use": {
|
|
3045
3693
|
_style = parseStyle(node, _style, stylesheets);
|
|
3046
|
-
const href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href") || "";
|
|
3694
|
+
const href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href") || node.getAttribute("href") || "";
|
|
3047
3695
|
const usedNodeId = href.substring(1);
|
|
3048
3696
|
const usedNode = node.viewportElement?.getElementById(usedNodeId);
|
|
3049
3697
|
if (usedNode) {
|
|
@@ -3114,46 +3762,7 @@ ${xml}`);
|
|
|
3114
3762
|
}
|
|
3115
3763
|
}
|
|
3116
3764
|
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;
|
|
3765
|
+
return new Path2DSet(parseNode(parseSVGToDOM(svg), {}));
|
|
3157
3766
|
}
|
|
3158
3767
|
|
|
3159
|
-
export { BoundingBox,
|
|
3768
|
+
export { ArcCurve, BoundingBox, CompositeCurve, CubicBezierCurve, Curve, CurvePath, EllipseCurve, LineCurve, Matrix3, Path2D, Path2DSet, PloygonCurve, QuadraticBezierCurve, RectangleCurve, SplineCurve, Vector2, catmullRom, cubicBezier, drawPoint, fillTriangulate, getAdaptiveCubicBezierCurvePoints, getAdaptiveQuadraticBezierCurvePoints, parseArcCommand, parsePathDataArgs, parseSVG, parseSVGToDOM, quadraticBezier, setCanvasContext, strokeTriangulate, svgPathCommandsAddToPath2D, svgPathCommandsToData, svgPathDataToCommands };
|