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