modern-path2d 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/index.cjs +1517 -900
- package/dist/index.d.cts +253 -254
- package/dist/index.d.mts +253 -254
- package/dist/index.d.ts +253 -254
- package/dist/index.js +2 -2
- package/dist/index.mjs +1499 -890
- package/package.json +7 -3
package/dist/index.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,684 @@ function pathDataToPathCommands(d) {
|
|
|
992
1004
|
return commands;
|
|
993
1005
|
}
|
|
994
1006
|
|
|
1007
|
+
function catmullRom(t, p0, p1, p2, p3) {
|
|
1008
|
+
const v0 = (p2 - p0) * 0.5;
|
|
1009
|
+
const v1 = (p3 - p1) * 0.5;
|
|
1010
|
+
const t2 = t * t;
|
|
1011
|
+
const t3 = t * t2;
|
|
1012
|
+
return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
function cubicBezierP0(t, p) {
|
|
1016
|
+
const k = 1 - t;
|
|
1017
|
+
return k * k * k * p;
|
|
1018
|
+
}
|
|
1019
|
+
function cubicBezierP1(t, p) {
|
|
1020
|
+
const k = 1 - t;
|
|
1021
|
+
return 3 * k * k * t * p;
|
|
1022
|
+
}
|
|
1023
|
+
function cubicBezierP2(t, p) {
|
|
1024
|
+
return 3 * (1 - t) * t * t * p;
|
|
1025
|
+
}
|
|
1026
|
+
function cubicBezierP3(t, p) {
|
|
1027
|
+
return t * t * t * p;
|
|
1028
|
+
}
|
|
1029
|
+
function cubicBezier(t, p0, p1, p2, p3) {
|
|
1030
|
+
return cubicBezierP0(t, p0) + cubicBezierP1(t, p1) + cubicBezierP2(t, p2) + cubicBezierP3(t, p3);
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
function fillTriangulate(points, options = {}) {
|
|
1034
|
+
let {
|
|
1035
|
+
vertices = [],
|
|
1036
|
+
indices = [],
|
|
1037
|
+
holes = [],
|
|
1038
|
+
verticesStride = 2,
|
|
1039
|
+
verticesOffset = 0,
|
|
1040
|
+
indicesOffset = 0
|
|
1041
|
+
} = options;
|
|
1042
|
+
const triangles = earcut__default(points, holes, 2);
|
|
1043
|
+
if (triangles) {
|
|
1044
|
+
for (let i = 0; i < triangles.length; i += 3) {
|
|
1045
|
+
indices[indicesOffset++] = triangles[i] + verticesOffset;
|
|
1046
|
+
indices[indicesOffset++] = triangles[i + 1] + verticesOffset;
|
|
1047
|
+
indices[indicesOffset++] = triangles[i + 2] + verticesOffset;
|
|
1048
|
+
}
|
|
1049
|
+
let index = verticesOffset * verticesStride;
|
|
1050
|
+
for (let i = 0; i < points.length; i += 2) {
|
|
1051
|
+
vertices[index] = points[i];
|
|
1052
|
+
vertices[index + 1] = points[i + 1];
|
|
1053
|
+
index += verticesStride;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
return {
|
|
1057
|
+
vertices,
|
|
1058
|
+
indices
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
const RECURSION_LIMIT$1 = 8;
|
|
1063
|
+
const FLT_EPSILON$1 = 11920929e-14;
|
|
1064
|
+
const PATH_DISTANCE_EPSILON$1 = 1;
|
|
1065
|
+
function getAdaptiveCubicBezierCurvePoints(sX, sY, x1, y1, x2, y2, x, y, smoothness = 0.5, points) {
|
|
1066
|
+
const scale = 1;
|
|
1067
|
+
const smoothing = Math.min(
|
|
1068
|
+
0.99,
|
|
1069
|
+
// a value of 1.0 actually inverts smoothing, so we cap it at 0.99
|
|
1070
|
+
Math.max(0, smoothness)
|
|
1071
|
+
);
|
|
1072
|
+
let distanceTolerance = (PATH_DISTANCE_EPSILON$1 - smoothing) / scale;
|
|
1073
|
+
distanceTolerance *= distanceTolerance;
|
|
1074
|
+
recursive$1(sX, sY, x1, y1, x2, y2, x, y, points, distanceTolerance, 0);
|
|
1075
|
+
points.push(x, y);
|
|
1076
|
+
return points;
|
|
1077
|
+
}
|
|
1078
|
+
function recursive$1(x1, y1, x2, y2, x3, y3, x4, y4, points, distanceTolerance, level) {
|
|
1079
|
+
if (level > RECURSION_LIMIT$1)
|
|
1080
|
+
return;
|
|
1081
|
+
const x12 = (x1 + x2) / 2;
|
|
1082
|
+
const y12 = (y1 + y2) / 2;
|
|
1083
|
+
const x23 = (x2 + x3) / 2;
|
|
1084
|
+
const y23 = (y2 + y3) / 2;
|
|
1085
|
+
const x34 = (x3 + x4) / 2;
|
|
1086
|
+
const y34 = (y3 + y4) / 2;
|
|
1087
|
+
const x123 = (x12 + x23) / 2;
|
|
1088
|
+
const y123 = (y12 + y23) / 2;
|
|
1089
|
+
const x234 = (x23 + x34) / 2;
|
|
1090
|
+
const y234 = (y23 + y34) / 2;
|
|
1091
|
+
const x1234 = (x123 + x234) / 2;
|
|
1092
|
+
const y1234 = (y123 + y234) / 2;
|
|
1093
|
+
if (level > 0) {
|
|
1094
|
+
let dx = x4 - x1;
|
|
1095
|
+
let dy = y4 - y1;
|
|
1096
|
+
const d2 = Math.abs((x2 - x4) * dy - (y2 - y4) * dx);
|
|
1097
|
+
const d3 = Math.abs((x3 - x4) * dy - (y3 - y4) * dx);
|
|
1098
|
+
if (d2 > FLT_EPSILON$1 && d3 > FLT_EPSILON$1) {
|
|
1099
|
+
if ((d2 + d3) * (d2 + d3) <= distanceTolerance * (dx * dx + dy * dy)) {
|
|
1100
|
+
{
|
|
1101
|
+
points.push(x1234, y1234);
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
} else if (d2 > FLT_EPSILON$1) {
|
|
1106
|
+
if (d2 * d2 <= distanceTolerance * (dx * dx + dy * dy)) {
|
|
1107
|
+
{
|
|
1108
|
+
points.push(x1234, y1234);
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
} else if (d3 > FLT_EPSILON$1) {
|
|
1113
|
+
if (d3 * d3 <= distanceTolerance * (dx * dx + dy * dy)) {
|
|
1114
|
+
{
|
|
1115
|
+
points.push(x1234, y1234);
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
} else {
|
|
1120
|
+
dx = x1234 - (x1 + x4) / 2;
|
|
1121
|
+
dy = y1234 - (y1 + y4) / 2;
|
|
1122
|
+
if (dx * dx + dy * dy <= distanceTolerance) {
|
|
1123
|
+
points.push(x1234, y1234);
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
recursive$1(x1, y1, x12, y12, x123, y123, x1234, y1234, points, distanceTolerance, level + 1);
|
|
1129
|
+
recursive$1(x1234, y1234, x234, y234, x34, y34, x4, y4, points, distanceTolerance, level + 1);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
const RECURSION_LIMIT = 8;
|
|
1133
|
+
const FLT_EPSILON = 11920929e-14;
|
|
1134
|
+
const PATH_DISTANCE_EPSILON = 1;
|
|
1135
|
+
function getAdaptiveQuadraticBezierCurvePoints(sX, sY, x1, y1, x, y, smoothness = 0.5, points) {
|
|
1136
|
+
const scale = 1;
|
|
1137
|
+
const smoothing = Math.min(
|
|
1138
|
+
0.99,
|
|
1139
|
+
// a value of 1.0 actually inverts smoothing, so we cap it at 0.99
|
|
1140
|
+
Math.max(0, smoothness)
|
|
1141
|
+
);
|
|
1142
|
+
let distanceTolerance = (PATH_DISTANCE_EPSILON - smoothing) / scale;
|
|
1143
|
+
distanceTolerance *= distanceTolerance;
|
|
1144
|
+
recursive(points, sX, sY, x1, y1, x, y, distanceTolerance, 0);
|
|
1145
|
+
points.push(x, y);
|
|
1146
|
+
return points;
|
|
1147
|
+
}
|
|
1148
|
+
function recursive(points, x1, y1, x2, y2, x3, y3, distanceTolerance, level) {
|
|
1149
|
+
if (level > RECURSION_LIMIT)
|
|
1150
|
+
return;
|
|
1151
|
+
const x12 = (x1 + x2) / 2;
|
|
1152
|
+
const y12 = (y1 + y2) / 2;
|
|
1153
|
+
const x23 = (x2 + x3) / 2;
|
|
1154
|
+
const y23 = (y2 + y3) / 2;
|
|
1155
|
+
const x123 = (x12 + x23) / 2;
|
|
1156
|
+
const y123 = (y12 + y23) / 2;
|
|
1157
|
+
let dx = x3 - x1;
|
|
1158
|
+
let dy = y3 - y1;
|
|
1159
|
+
const d = Math.abs((x2 - x3) * dy - (y2 - y3) * dx);
|
|
1160
|
+
if (d > FLT_EPSILON) {
|
|
1161
|
+
if (d * d <= distanceTolerance * (dx * dx + dy * dy)) {
|
|
1162
|
+
{
|
|
1163
|
+
points.push(x123, y123);
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
} else {
|
|
1168
|
+
dx = x123 - (x1 + x3) / 2;
|
|
1169
|
+
dy = y123 - (y1 + y3) / 2;
|
|
1170
|
+
if (dx * dx + dy * dy <= distanceTolerance) {
|
|
1171
|
+
points.push(x123, y123);
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
recursive(points, x1, y1, x12, y12, x123, y123, distanceTolerance, level + 1);
|
|
1176
|
+
recursive(points, x123, y123, x23, y23, x3, y3, distanceTolerance, level + 1);
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
function quadraticBezierP0(t, p) {
|
|
1180
|
+
const k = 1 - t;
|
|
1181
|
+
return k * k * p;
|
|
1182
|
+
}
|
|
1183
|
+
function quadraticBezierP1(t, p) {
|
|
1184
|
+
return 2 * (1 - t) * t * p;
|
|
1185
|
+
}
|
|
1186
|
+
function quadraticBezierP2(t, p) {
|
|
1187
|
+
return t * t * p;
|
|
1188
|
+
}
|
|
1189
|
+
function quadraticBezier(t, p0, p1, p2) {
|
|
1190
|
+
return quadraticBezierP0(t, p0) + quadraticBezierP1(t, p1) + quadraticBezierP2(t, p2);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
const closePointEps = 1e-4;
|
|
1194
|
+
const curveEps = 1e-4;
|
|
1195
|
+
function strokeTriangulate(points, options = {}) {
|
|
1196
|
+
const {
|
|
1197
|
+
vertices = [],
|
|
1198
|
+
indices = [],
|
|
1199
|
+
lineStyle = {
|
|
1200
|
+
alignment: 0.5,
|
|
1201
|
+
cap: "butt",
|
|
1202
|
+
join: "miter",
|
|
1203
|
+
width: 1,
|
|
1204
|
+
miterLimit: 10
|
|
1205
|
+
},
|
|
1206
|
+
flipAlignment = false,
|
|
1207
|
+
closed = true
|
|
1208
|
+
} = options;
|
|
1209
|
+
const eps = closePointEps;
|
|
1210
|
+
if (points.length === 0) {
|
|
1211
|
+
return { vertices, indices };
|
|
1212
|
+
}
|
|
1213
|
+
const style = lineStyle;
|
|
1214
|
+
let alignment = style.alignment;
|
|
1215
|
+
if (lineStyle.alignment !== 0.5) {
|
|
1216
|
+
let orientation = getOrientationOfPoints(points);
|
|
1217
|
+
if (flipAlignment)
|
|
1218
|
+
orientation *= -1;
|
|
1219
|
+
alignment = (alignment - 0.5) * orientation + 0.5;
|
|
1220
|
+
}
|
|
1221
|
+
const firstPoint = { x: points[0], y: points[1] };
|
|
1222
|
+
const lastPoint = { x: points[points.length - 2], y: points[points.length - 1] };
|
|
1223
|
+
const closedShape = closed;
|
|
1224
|
+
const closedPath = Math.abs(firstPoint.x - lastPoint.x) < eps && Math.abs(firstPoint.y - lastPoint.y) < eps;
|
|
1225
|
+
if (closedShape) {
|
|
1226
|
+
points = points.slice();
|
|
1227
|
+
if (closedPath) {
|
|
1228
|
+
points.pop();
|
|
1229
|
+
points.pop();
|
|
1230
|
+
lastPoint.x = points[points.length - 2];
|
|
1231
|
+
lastPoint.y = points[points.length - 1];
|
|
1232
|
+
}
|
|
1233
|
+
const midPointX = (firstPoint.x + lastPoint.x) * 0.5;
|
|
1234
|
+
const midPointY = (lastPoint.y + firstPoint.y) * 0.5;
|
|
1235
|
+
points.unshift(midPointX, midPointY);
|
|
1236
|
+
points.push(midPointX, midPointY);
|
|
1237
|
+
}
|
|
1238
|
+
const verts = vertices;
|
|
1239
|
+
const length = points.length / 2;
|
|
1240
|
+
let indexCount = points.length;
|
|
1241
|
+
const indexStart = verts.length / 2;
|
|
1242
|
+
const width = style.width / 2;
|
|
1243
|
+
const widthSquared = width * width;
|
|
1244
|
+
const miterLimitSquared = style.miterLimit * style.miterLimit;
|
|
1245
|
+
let x0 = points[0];
|
|
1246
|
+
let y0 = points[1];
|
|
1247
|
+
let x1 = points[2];
|
|
1248
|
+
let y1 = points[3];
|
|
1249
|
+
let x2 = 0;
|
|
1250
|
+
let y2 = 0;
|
|
1251
|
+
let perpX = -(y0 - y1);
|
|
1252
|
+
let perpY = x0 - x1;
|
|
1253
|
+
let perp1x = 0;
|
|
1254
|
+
let perp1y = 0;
|
|
1255
|
+
let dist = Math.sqrt(perpX * perpX + perpY * perpY);
|
|
1256
|
+
perpX /= dist;
|
|
1257
|
+
perpY /= dist;
|
|
1258
|
+
perpX *= width;
|
|
1259
|
+
perpY *= width;
|
|
1260
|
+
const ratio = alignment;
|
|
1261
|
+
const innerWeight = (1 - ratio) * 2;
|
|
1262
|
+
const outerWeight = ratio * 2;
|
|
1263
|
+
if (!closedShape) {
|
|
1264
|
+
if (style.cap === "round") {
|
|
1265
|
+
indexCount += round(
|
|
1266
|
+
x0 - perpX * (innerWeight - outerWeight) * 0.5,
|
|
1267
|
+
y0 - perpY * (innerWeight - outerWeight) * 0.5,
|
|
1268
|
+
x0 - perpX * innerWeight,
|
|
1269
|
+
y0 - perpY * innerWeight,
|
|
1270
|
+
x0 + perpX * outerWeight,
|
|
1271
|
+
y0 + perpY * outerWeight,
|
|
1272
|
+
verts,
|
|
1273
|
+
true
|
|
1274
|
+
) + 2;
|
|
1275
|
+
} else if (style.cap === "square") {
|
|
1276
|
+
indexCount += square(x0, y0, perpX, perpY, innerWeight, outerWeight, true, verts);
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
verts.push(
|
|
1280
|
+
x0 - perpX * innerWeight,
|
|
1281
|
+
y0 - perpY * innerWeight
|
|
1282
|
+
);
|
|
1283
|
+
verts.push(
|
|
1284
|
+
x0 + perpX * outerWeight,
|
|
1285
|
+
y0 + perpY * outerWeight
|
|
1286
|
+
);
|
|
1287
|
+
for (let i = 1; i < length - 1; ++i) {
|
|
1288
|
+
x0 = points[(i - 1) * 2];
|
|
1289
|
+
y0 = points[(i - 1) * 2 + 1];
|
|
1290
|
+
x1 = points[i * 2];
|
|
1291
|
+
y1 = points[i * 2 + 1];
|
|
1292
|
+
x2 = points[(i + 1) * 2];
|
|
1293
|
+
y2 = points[(i + 1) * 2 + 1];
|
|
1294
|
+
perpX = -(y0 - y1);
|
|
1295
|
+
perpY = x0 - x1;
|
|
1296
|
+
dist = Math.sqrt(perpX * perpX + perpY * perpY);
|
|
1297
|
+
perpX /= dist;
|
|
1298
|
+
perpY /= dist;
|
|
1299
|
+
perpX *= width;
|
|
1300
|
+
perpY *= width;
|
|
1301
|
+
perp1x = -(y1 - y2);
|
|
1302
|
+
perp1y = x1 - x2;
|
|
1303
|
+
dist = Math.sqrt(perp1x * perp1x + perp1y * perp1y);
|
|
1304
|
+
perp1x /= dist;
|
|
1305
|
+
perp1y /= dist;
|
|
1306
|
+
perp1x *= width;
|
|
1307
|
+
perp1y *= width;
|
|
1308
|
+
const dx0 = x1 - x0;
|
|
1309
|
+
const dy0 = y0 - y1;
|
|
1310
|
+
const dx1 = x1 - x2;
|
|
1311
|
+
const dy1 = y2 - y1;
|
|
1312
|
+
const dot = dx0 * dx1 + dy0 * dy1;
|
|
1313
|
+
const cross = dy0 * dx1 - dy1 * dx0;
|
|
1314
|
+
const clockwise = cross < 0;
|
|
1315
|
+
if (Math.abs(cross) < 1e-3 * Math.abs(dot)) {
|
|
1316
|
+
verts.push(
|
|
1317
|
+
x1 - perpX * innerWeight,
|
|
1318
|
+
y1 - perpY * innerWeight
|
|
1319
|
+
);
|
|
1320
|
+
verts.push(
|
|
1321
|
+
x1 + perpX * outerWeight,
|
|
1322
|
+
y1 + perpY * outerWeight
|
|
1323
|
+
);
|
|
1324
|
+
if (dot >= 0) {
|
|
1325
|
+
if (style.join === "round") {
|
|
1326
|
+
indexCount += round(
|
|
1327
|
+
x1,
|
|
1328
|
+
y1,
|
|
1329
|
+
x1 - perpX * innerWeight,
|
|
1330
|
+
y1 - perpY * innerWeight,
|
|
1331
|
+
x1 - perp1x * innerWeight,
|
|
1332
|
+
y1 - perp1y * innerWeight,
|
|
1333
|
+
verts,
|
|
1334
|
+
false
|
|
1335
|
+
) + 4;
|
|
1336
|
+
} else {
|
|
1337
|
+
indexCount += 2;
|
|
1338
|
+
}
|
|
1339
|
+
verts.push(
|
|
1340
|
+
x1 - perp1x * outerWeight,
|
|
1341
|
+
y1 - perp1y * outerWeight
|
|
1342
|
+
);
|
|
1343
|
+
verts.push(
|
|
1344
|
+
x1 + perp1x * innerWeight,
|
|
1345
|
+
y1 + perp1y * innerWeight
|
|
1346
|
+
);
|
|
1347
|
+
}
|
|
1348
|
+
continue;
|
|
1349
|
+
}
|
|
1350
|
+
const c1 = (-perpX + x0) * (-perpY + y1) - (-perpX + x1) * (-perpY + y0);
|
|
1351
|
+
const c2 = (-perp1x + x2) * (-perp1y + y1) - (-perp1x + x1) * (-perp1y + y2);
|
|
1352
|
+
const px = (dx0 * c2 - dx1 * c1) / cross;
|
|
1353
|
+
const py = (dy1 * c1 - dy0 * c2) / cross;
|
|
1354
|
+
const pDist = (px - x1) * (px - x1) + (py - y1) * (py - y1);
|
|
1355
|
+
const imx = x1 + (px - x1) * innerWeight;
|
|
1356
|
+
const imy = y1 + (py - y1) * innerWeight;
|
|
1357
|
+
const omx = x1 - (px - x1) * outerWeight;
|
|
1358
|
+
const omy = y1 - (py - y1) * outerWeight;
|
|
1359
|
+
const smallerInsideSegmentSq = Math.min(dx0 * dx0 + dy0 * dy0, dx1 * dx1 + dy1 * dy1);
|
|
1360
|
+
const insideWeight = clockwise ? innerWeight : outerWeight;
|
|
1361
|
+
const smallerInsideDiagonalSq = smallerInsideSegmentSq + insideWeight * insideWeight * widthSquared;
|
|
1362
|
+
const insideMiterOk = pDist <= smallerInsideDiagonalSq;
|
|
1363
|
+
if (insideMiterOk) {
|
|
1364
|
+
if (style.join === "bevel" || pDist / widthSquared > miterLimitSquared) {
|
|
1365
|
+
if (clockwise) {
|
|
1366
|
+
verts.push(imx, imy);
|
|
1367
|
+
verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
|
|
1368
|
+
verts.push(imx, imy);
|
|
1369
|
+
verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
|
|
1370
|
+
} else {
|
|
1371
|
+
verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
|
|
1372
|
+
verts.push(omx, omy);
|
|
1373
|
+
verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
|
|
1374
|
+
verts.push(omx, omy);
|
|
1375
|
+
}
|
|
1376
|
+
indexCount += 2;
|
|
1377
|
+
} else if (style.join === "round") {
|
|
1378
|
+
if (clockwise) {
|
|
1379
|
+
verts.push(imx, imy);
|
|
1380
|
+
verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
|
|
1381
|
+
indexCount += round(
|
|
1382
|
+
x1,
|
|
1383
|
+
y1,
|
|
1384
|
+
x1 + perpX * outerWeight,
|
|
1385
|
+
y1 + perpY * outerWeight,
|
|
1386
|
+
x1 + perp1x * outerWeight,
|
|
1387
|
+
y1 + perp1y * outerWeight,
|
|
1388
|
+
verts,
|
|
1389
|
+
true
|
|
1390
|
+
) + 4;
|
|
1391
|
+
verts.push(imx, imy);
|
|
1392
|
+
verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
|
|
1393
|
+
} else {
|
|
1394
|
+
verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
|
|
1395
|
+
verts.push(omx, omy);
|
|
1396
|
+
indexCount += round(
|
|
1397
|
+
x1,
|
|
1398
|
+
y1,
|
|
1399
|
+
x1 - perpX * innerWeight,
|
|
1400
|
+
y1 - perpY * innerWeight,
|
|
1401
|
+
x1 - perp1x * innerWeight,
|
|
1402
|
+
y1 - perp1y * innerWeight,
|
|
1403
|
+
verts,
|
|
1404
|
+
false
|
|
1405
|
+
) + 4;
|
|
1406
|
+
verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
|
|
1407
|
+
verts.push(omx, omy);
|
|
1408
|
+
}
|
|
1409
|
+
} else {
|
|
1410
|
+
verts.push(imx, imy);
|
|
1411
|
+
verts.push(omx, omy);
|
|
1412
|
+
}
|
|
1413
|
+
} else {
|
|
1414
|
+
verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
|
|
1415
|
+
verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
|
|
1416
|
+
if (style.join === "round") {
|
|
1417
|
+
if (clockwise) {
|
|
1418
|
+
indexCount += round(
|
|
1419
|
+
x1,
|
|
1420
|
+
y1,
|
|
1421
|
+
x1 + perpX * outerWeight,
|
|
1422
|
+
y1 + perpY * outerWeight,
|
|
1423
|
+
x1 + perp1x * outerWeight,
|
|
1424
|
+
y1 + perp1y * outerWeight,
|
|
1425
|
+
verts,
|
|
1426
|
+
true
|
|
1427
|
+
) + 2;
|
|
1428
|
+
} else {
|
|
1429
|
+
indexCount += round(
|
|
1430
|
+
x1,
|
|
1431
|
+
y1,
|
|
1432
|
+
x1 - perpX * innerWeight,
|
|
1433
|
+
y1 - perpY * innerWeight,
|
|
1434
|
+
x1 - perp1x * innerWeight,
|
|
1435
|
+
y1 - perp1y * innerWeight,
|
|
1436
|
+
verts,
|
|
1437
|
+
false
|
|
1438
|
+
) + 2;
|
|
1439
|
+
}
|
|
1440
|
+
} else if (style.join === "miter" && pDist / widthSquared <= miterLimitSquared) {
|
|
1441
|
+
if (clockwise) {
|
|
1442
|
+
verts.push(omx, omy);
|
|
1443
|
+
verts.push(omx, omy);
|
|
1444
|
+
} else {
|
|
1445
|
+
verts.push(imx, imy);
|
|
1446
|
+
verts.push(imx, imy);
|
|
1447
|
+
}
|
|
1448
|
+
indexCount += 2;
|
|
1449
|
+
}
|
|
1450
|
+
verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
|
|
1451
|
+
verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
|
|
1452
|
+
indexCount += 2;
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
x0 = points[(length - 2) * 2];
|
|
1456
|
+
y0 = points[(length - 2) * 2 + 1];
|
|
1457
|
+
x1 = points[(length - 1) * 2];
|
|
1458
|
+
y1 = points[(length - 1) * 2 + 1];
|
|
1459
|
+
perpX = -(y0 - y1);
|
|
1460
|
+
perpY = x0 - x1;
|
|
1461
|
+
dist = Math.sqrt(perpX * perpX + perpY * perpY);
|
|
1462
|
+
perpX /= dist;
|
|
1463
|
+
perpY /= dist;
|
|
1464
|
+
perpX *= width;
|
|
1465
|
+
perpY *= width;
|
|
1466
|
+
verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
|
|
1467
|
+
verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
|
|
1468
|
+
if (!closedShape) {
|
|
1469
|
+
if (style.cap === "round") {
|
|
1470
|
+
indexCount += round(
|
|
1471
|
+
x1 - perpX * (innerWeight - outerWeight) * 0.5,
|
|
1472
|
+
y1 - perpY * (innerWeight - outerWeight) * 0.5,
|
|
1473
|
+
x1 - perpX * innerWeight,
|
|
1474
|
+
y1 - perpY * innerWeight,
|
|
1475
|
+
x1 + perpX * outerWeight,
|
|
1476
|
+
y1 + perpY * outerWeight,
|
|
1477
|
+
verts,
|
|
1478
|
+
false
|
|
1479
|
+
) + 2;
|
|
1480
|
+
} else if (style.cap === "square") {
|
|
1481
|
+
indexCount += square(x1, y1, perpX, perpY, innerWeight, outerWeight, false, verts);
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
const eps2 = curveEps * curveEps;
|
|
1485
|
+
for (let i = indexStart; i < indexCount + indexStart - 2; ++i) {
|
|
1486
|
+
x0 = verts[i * 2];
|
|
1487
|
+
y0 = verts[i * 2 + 1];
|
|
1488
|
+
x1 = verts[(i + 1) * 2];
|
|
1489
|
+
y1 = verts[(i + 1) * 2 + 1];
|
|
1490
|
+
x2 = verts[(i + 2) * 2];
|
|
1491
|
+
y2 = verts[(i + 2) * 2 + 1];
|
|
1492
|
+
if (Math.abs(x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)) < eps2) {
|
|
1493
|
+
continue;
|
|
1494
|
+
}
|
|
1495
|
+
indices.push(i, i + 1, i + 2);
|
|
1496
|
+
}
|
|
1497
|
+
return {
|
|
1498
|
+
vertices,
|
|
1499
|
+
indices
|
|
1500
|
+
};
|
|
1501
|
+
}
|
|
1502
|
+
function getOrientationOfPoints(points) {
|
|
1503
|
+
const m = points.length;
|
|
1504
|
+
if (m < 6) {
|
|
1505
|
+
return 1;
|
|
1506
|
+
}
|
|
1507
|
+
let area = 0;
|
|
1508
|
+
for (let i = 0, x1 = points[m - 2], y1 = points[m - 1]; i < m; i += 2) {
|
|
1509
|
+
const x2 = points[i];
|
|
1510
|
+
const y2 = points[i + 1];
|
|
1511
|
+
area += (x2 - x1) * (y2 + y1);
|
|
1512
|
+
x1 = x2;
|
|
1513
|
+
y1 = y2;
|
|
1514
|
+
}
|
|
1515
|
+
if (area < 0) {
|
|
1516
|
+
return -1;
|
|
1517
|
+
}
|
|
1518
|
+
return 1;
|
|
1519
|
+
}
|
|
1520
|
+
function square(x, y, nx, ny, innerWeight, outerWeight, clockwise, verts) {
|
|
1521
|
+
const ix = x - nx * innerWeight;
|
|
1522
|
+
const iy = y - ny * innerWeight;
|
|
1523
|
+
const ox = x + nx * outerWeight;
|
|
1524
|
+
const oy = y + ny * outerWeight;
|
|
1525
|
+
let exx;
|
|
1526
|
+
let eyy;
|
|
1527
|
+
if (clockwise) {
|
|
1528
|
+
exx = ny;
|
|
1529
|
+
eyy = -nx;
|
|
1530
|
+
} else {
|
|
1531
|
+
exx = -ny;
|
|
1532
|
+
eyy = nx;
|
|
1533
|
+
}
|
|
1534
|
+
const eix = ix + exx;
|
|
1535
|
+
const eiy = iy + eyy;
|
|
1536
|
+
const eox = ox + exx;
|
|
1537
|
+
const eoy = oy + eyy;
|
|
1538
|
+
verts.push(eix, eiy);
|
|
1539
|
+
verts.push(eox, eoy);
|
|
1540
|
+
return 2;
|
|
1541
|
+
}
|
|
1542
|
+
function round(cx, cy, sx, sy, ex, ey, verts, clockwise) {
|
|
1543
|
+
const cx2p0x = sx - cx;
|
|
1544
|
+
const cy2p0y = sy - cy;
|
|
1545
|
+
let angle0 = Math.atan2(cx2p0x, cy2p0y);
|
|
1546
|
+
let angle1 = Math.atan2(ex - cx, ey - cy);
|
|
1547
|
+
if (clockwise && angle0 < angle1) {
|
|
1548
|
+
angle0 += Math.PI * 2;
|
|
1549
|
+
} else if (!clockwise && angle0 > angle1) {
|
|
1550
|
+
angle1 += Math.PI * 2;
|
|
1551
|
+
}
|
|
1552
|
+
let startAngle = angle0;
|
|
1553
|
+
const angleDiff = angle1 - angle0;
|
|
1554
|
+
const absAngleDiff = Math.abs(angleDiff);
|
|
1555
|
+
const radius = Math.sqrt(cx2p0x * cx2p0x + cy2p0y * cy2p0y);
|
|
1556
|
+
const segCount = (15 * absAngleDiff * Math.sqrt(radius) / Math.PI >> 0) + 1;
|
|
1557
|
+
const angleInc = angleDiff / segCount;
|
|
1558
|
+
startAngle += angleInc;
|
|
1559
|
+
if (clockwise) {
|
|
1560
|
+
verts.push(cx, cy);
|
|
1561
|
+
verts.push(sx, sy);
|
|
1562
|
+
for (let i = 1, angle = startAngle; i < segCount; i++, angle += angleInc) {
|
|
1563
|
+
verts.push(cx, cy);
|
|
1564
|
+
verts.push(cx + Math.sin(angle) * radius, cy + Math.cos(angle) * radius);
|
|
1565
|
+
}
|
|
1566
|
+
verts.push(cx, cy);
|
|
1567
|
+
verts.push(ex, ey);
|
|
1568
|
+
} else {
|
|
1569
|
+
verts.push(sx, sy);
|
|
1570
|
+
verts.push(cx, cy);
|
|
1571
|
+
for (let i = 1, angle = startAngle; i < segCount; i++, angle += angleInc) {
|
|
1572
|
+
verts.push(cx + Math.sin(angle) * radius, cy + Math.cos(angle) * radius);
|
|
1573
|
+
verts.push(cx, cy);
|
|
1574
|
+
}
|
|
1575
|
+
verts.push(ex, ey);
|
|
1576
|
+
verts.push(cx, cy);
|
|
1577
|
+
}
|
|
1578
|
+
return segCount * 2;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
995
1581
|
class Curve {
|
|
996
|
-
|
|
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
|
-
const
|
|
1010
|
-
for (let i = 0; i <=
|
|
1011
|
-
|
|
1598
|
+
getUnevenPointArray(count = 5, output = []) {
|
|
1599
|
+
const p = new Vector2();
|
|
1600
|
+
for (let i = 0, len = Math.max(1, count) - 1; i <= len; i++) {
|
|
1601
|
+
this.getPoint(i / len, p);
|
|
1602
|
+
output.push(p.x, p.y);
|
|
1603
|
+
}
|
|
1604
|
+
return output;
|
|
1605
|
+
}
|
|
1606
|
+
getSpacedPointArray(count = 5, output = []) {
|
|
1607
|
+
const p = new Vector2();
|
|
1608
|
+
for (let i = 0, len = Math.max(1, count) - 1; i <= len; i++) {
|
|
1609
|
+
this.getPointAt(i / len, p);
|
|
1610
|
+
output.push(p.x, p.y);
|
|
1012
1611
|
}
|
|
1013
|
-
return
|
|
1612
|
+
return output;
|
|
1014
1613
|
}
|
|
1015
|
-
|
|
1016
|
-
this.
|
|
1017
|
-
return this;
|
|
1614
|
+
getAdaptivePointArray(output = []) {
|
|
1615
|
+
return this.getUnevenPointArray(5, output);
|
|
1018
1616
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1617
|
+
_pointArrayToPoint(array, output = []) {
|
|
1618
|
+
for (let i = 0, len = array.length; i < len; i += 2) {
|
|
1619
|
+
const x = array[i];
|
|
1620
|
+
const y = array[i + 1];
|
|
1621
|
+
output.push(new Vector2(x, y));
|
|
1622
|
+
}
|
|
1623
|
+
return output;
|
|
1624
|
+
}
|
|
1625
|
+
getSpacedPoints(count, output = []) {
|
|
1626
|
+
const array = this.getSpacedPointArray(count);
|
|
1627
|
+
this._pointArrayToPoint(array, output);
|
|
1628
|
+
return output;
|
|
1629
|
+
}
|
|
1630
|
+
getUnevenPoints(count, output = []) {
|
|
1631
|
+
const array = this.getUnevenPointArray(count);
|
|
1632
|
+
this._pointArrayToPoint(array, output);
|
|
1633
|
+
return output;
|
|
1634
|
+
}
|
|
1635
|
+
getAdaptivePoints(output = []) {
|
|
1636
|
+
const array = this.getAdaptivePointArray();
|
|
1637
|
+
this._pointArrayToPoint(array, output);
|
|
1638
|
+
return output;
|
|
1639
|
+
}
|
|
1640
|
+
getPoints(count, output = []) {
|
|
1641
|
+
let array;
|
|
1642
|
+
if (count) {
|
|
1643
|
+
array = this.getUnevenPointArray(count);
|
|
1644
|
+
} else {
|
|
1645
|
+
array = this.getAdaptivePointArray();
|
|
1023
1646
|
}
|
|
1024
|
-
|
|
1647
|
+
this._pointArrayToPoint(array, output);
|
|
1648
|
+
return output;
|
|
1025
1649
|
}
|
|
1026
1650
|
getLength() {
|
|
1027
1651
|
const lengths = this.getLengths();
|
|
1028
1652
|
return lengths[lengths.length - 1];
|
|
1029
1653
|
}
|
|
1030
|
-
getLengths(
|
|
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();
|
|
1654
|
+
getLengths() {
|
|
1655
|
+
if (!this._arcLengths || this._arcLengths.length !== this.arcLengthDivision + 1) {
|
|
1656
|
+
this.updateLengths();
|
|
1657
|
+
}
|
|
1658
|
+
return this._arcLengths;
|
|
1659
|
+
}
|
|
1660
|
+
updateLengths() {
|
|
1661
|
+
const divisions = this.arcLengthDivision;
|
|
1662
|
+
const arcLengths = [0];
|
|
1663
|
+
for (let sum = 0, prev = this.getPoint(0), i = 1; i <= divisions; i++) {
|
|
1664
|
+
const current = this.getPoint(i / divisions);
|
|
1665
|
+
sum += current.distanceTo(prev);
|
|
1666
|
+
arcLengths.push(sum);
|
|
1667
|
+
prev = current;
|
|
1668
|
+
}
|
|
1669
|
+
this._arcLengths = arcLengths;
|
|
1052
1670
|
}
|
|
1053
1671
|
getUToTMapping(u, distance) {
|
|
1054
1672
|
const lengths = this.getLengths();
|
|
1055
|
-
let i = 0;
|
|
1056
1673
|
const lengthsLen = lengths.length;
|
|
1057
|
-
|
|
1058
|
-
if (
|
|
1059
|
-
|
|
1060
|
-
} else {
|
|
1061
|
-
targetLength = u * lengths[lengthsLen - 1];
|
|
1674
|
+
const targetLen = distance ?? u * lengths[lengthsLen - 1];
|
|
1675
|
+
if (lengthsLen < 2) {
|
|
1676
|
+
return targetLen / lengths[0];
|
|
1062
1677
|
}
|
|
1678
|
+
let i = 0;
|
|
1063
1679
|
let low = 0;
|
|
1064
1680
|
let high = lengthsLen - 1;
|
|
1065
1681
|
let comparison;
|
|
1066
1682
|
while (low <= high) {
|
|
1067
1683
|
i = Math.floor(low + (high - low) / 2);
|
|
1068
|
-
comparison = lengths[i] -
|
|
1684
|
+
comparison = lengths[i] - targetLen;
|
|
1069
1685
|
if (comparison < 0) {
|
|
1070
1686
|
low = i + 1;
|
|
1071
1687
|
} else if (comparison > 0) {
|
|
@@ -1076,13 +1692,13 @@ class Curve {
|
|
|
1076
1692
|
}
|
|
1077
1693
|
}
|
|
1078
1694
|
i = high;
|
|
1079
|
-
if (lengths[i] ===
|
|
1695
|
+
if (lengths[i] === targetLen) {
|
|
1080
1696
|
return i / (lengthsLen - 1);
|
|
1081
1697
|
}
|
|
1082
|
-
const
|
|
1083
|
-
const
|
|
1084
|
-
const segmentLength =
|
|
1085
|
-
const segmentFraction = (
|
|
1698
|
+
const before = lengths[i];
|
|
1699
|
+
const after = lengths[i + 1];
|
|
1700
|
+
const segmentLength = after - before;
|
|
1701
|
+
const segmentFraction = (targetLen - before) / segmentLength;
|
|
1086
1702
|
return (i + segmentFraction) / (lengthsLen - 1);
|
|
1087
1703
|
}
|
|
1088
1704
|
getTangent(t, output = new Vector2()) {
|
|
@@ -1121,31 +1737,55 @@ class Curve {
|
|
|
1121
1737
|
return mid;
|
|
1122
1738
|
}
|
|
1123
1739
|
matrix(matrix) {
|
|
1124
|
-
this.
|
|
1740
|
+
this.getControlPointRefs().forEach((point) => point.applyMatrix3(matrix));
|
|
1125
1741
|
return this;
|
|
1126
1742
|
}
|
|
1127
1743
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1128
|
-
this.getPoints()
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1744
|
+
const potins = this.getPoints();
|
|
1745
|
+
for (let i = 0, len = potins.length; i < len; i++) {
|
|
1746
|
+
const p = potins[i];
|
|
1747
|
+
min.min(p);
|
|
1748
|
+
max.max(p);
|
|
1749
|
+
}
|
|
1132
1750
|
return { min, max };
|
|
1133
1751
|
}
|
|
1134
1752
|
getBoundingBox() {
|
|
1135
1753
|
const { min, max } = this.getMinMax();
|
|
1136
1754
|
return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
1137
1755
|
}
|
|
1756
|
+
fillTriangulate(options) {
|
|
1757
|
+
return fillTriangulate(
|
|
1758
|
+
this.getPoints().reduce((arr, p) => {
|
|
1759
|
+
arr.push(p.x, p.y);
|
|
1760
|
+
return arr;
|
|
1761
|
+
}, []),
|
|
1762
|
+
options
|
|
1763
|
+
);
|
|
1764
|
+
}
|
|
1765
|
+
strokeTriangulate(options) {
|
|
1766
|
+
return strokeTriangulate(
|
|
1767
|
+
this.getPoints().reduce((arr, p) => {
|
|
1768
|
+
arr.push(p.x, p.y);
|
|
1769
|
+
return arr;
|
|
1770
|
+
}, []),
|
|
1771
|
+
options
|
|
1772
|
+
);
|
|
1773
|
+
}
|
|
1138
1774
|
toCommands() {
|
|
1139
|
-
|
|
1775
|
+
const comds = [];
|
|
1776
|
+
const potins = this.getPoints();
|
|
1777
|
+
for (let i = 0, len = potins.length; i < len; i++) {
|
|
1778
|
+
const p = potins[i];
|
|
1140
1779
|
if (i === 0) {
|
|
1141
|
-
|
|
1780
|
+
comds.push({ type: "M", x: p.x, y: p.y });
|
|
1142
1781
|
} else {
|
|
1143
|
-
|
|
1782
|
+
comds.push({ type: "L", x: p.x, y: p.y });
|
|
1144
1783
|
}
|
|
1145
|
-
}
|
|
1784
|
+
}
|
|
1785
|
+
return comds;
|
|
1146
1786
|
}
|
|
1147
1787
|
toData() {
|
|
1148
|
-
return
|
|
1788
|
+
return svgPathCommandsToData(this.toCommands());
|
|
1149
1789
|
}
|
|
1150
1790
|
drawTo(ctx) {
|
|
1151
1791
|
this.toCommands().forEach((cmd) => {
|
|
@@ -1161,7 +1801,7 @@ class Curve {
|
|
|
1161
1801
|
return this;
|
|
1162
1802
|
}
|
|
1163
1803
|
copy(source) {
|
|
1164
|
-
this.
|
|
1804
|
+
this.arcLengthDivision = source.arcLengthDivision;
|
|
1165
1805
|
return this;
|
|
1166
1806
|
}
|
|
1167
1807
|
clone() {
|
|
@@ -1169,179 +1809,22 @@ class Curve {
|
|
|
1169
1809
|
}
|
|
1170
1810
|
}
|
|
1171
1811
|
|
|
1172
|
-
class CircleCurve extends Curve {
|
|
1173
|
-
constructor(center, radius, start = 0, end = Math.PI * 2) {
|
|
1174
|
-
super();
|
|
1175
|
-
this.center = center;
|
|
1176
|
-
this.radius = radius;
|
|
1177
|
-
this.start = start;
|
|
1178
|
-
this.end = end;
|
|
1179
|
-
}
|
|
1180
|
-
getPoint(t) {
|
|
1181
|
-
const { radius, center } = this;
|
|
1182
|
-
return center.clone().add(this.getNormal(t).clone().scale(radius));
|
|
1183
|
-
}
|
|
1184
|
-
getTangent(t, output = new Vector2()) {
|
|
1185
|
-
const { x, y } = this.getNormal(t);
|
|
1186
|
-
return output.set(-y, x);
|
|
1187
|
-
}
|
|
1188
|
-
getNormal(t, output = new Vector2()) {
|
|
1189
|
-
const { start, end } = this;
|
|
1190
|
-
const _t = t * (end - start) + start - 0.5 * Math.PI;
|
|
1191
|
-
return output.set(Math.cos(_t), Math.sin(_t));
|
|
1192
|
-
}
|
|
1193
|
-
getControlPoints() {
|
|
1194
|
-
return [this.center];
|
|
1195
|
-
}
|
|
1196
|
-
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1197
|
-
min.x = Math.min(min.x, this.center.x - this.radius);
|
|
1198
|
-
min.y = Math.min(min.y, this.center.y - this.radius);
|
|
1199
|
-
max.x = Math.max(max.x, this.center.x + this.radius);
|
|
1200
|
-
max.y = Math.max(max.y, this.center.y + this.radius);
|
|
1201
|
-
return { min, max };
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
function catmullRom(t, p0, p1, p2, p3) {
|
|
1206
|
-
const v0 = (p2 - p0) * 0.5;
|
|
1207
|
-
const v1 = (p3 - p1) * 0.5;
|
|
1208
|
-
const t2 = t * t;
|
|
1209
|
-
const t3 = t * t2;
|
|
1210
|
-
return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1;
|
|
1211
|
-
}
|
|
1212
|
-
function quadraticBezierP0(t, p) {
|
|
1213
|
-
const k = 1 - t;
|
|
1214
|
-
return k * k * p;
|
|
1215
|
-
}
|
|
1216
|
-
function quadraticBezierP1(t, p) {
|
|
1217
|
-
return 2 * (1 - t) * t * p;
|
|
1218
|
-
}
|
|
1219
|
-
function quadraticBezierP2(t, p) {
|
|
1220
|
-
return t * t * p;
|
|
1221
|
-
}
|
|
1222
|
-
function quadraticBezier(t, p0, p1, p2) {
|
|
1223
|
-
return quadraticBezierP0(t, p0) + quadraticBezierP1(t, p1) + quadraticBezierP2(t, p2);
|
|
1224
|
-
}
|
|
1225
|
-
function cubicBezierP0(t, p) {
|
|
1226
|
-
const k = 1 - t;
|
|
1227
|
-
return k * k * k * p;
|
|
1228
|
-
}
|
|
1229
|
-
function cubicBezierP1(t, p) {
|
|
1230
|
-
const k = 1 - t;
|
|
1231
|
-
return 3 * k * k * t * p;
|
|
1232
|
-
}
|
|
1233
|
-
function cubicBezierP2(t, p) {
|
|
1234
|
-
return 3 * (1 - t) * t * t * p;
|
|
1235
|
-
}
|
|
1236
|
-
function cubicBezierP3(t, p) {
|
|
1237
|
-
return t * t * t * p;
|
|
1238
|
-
}
|
|
1239
|
-
function cubicBezier(t, p0, p1, p2, p3) {
|
|
1240
|
-
return cubicBezierP0(t, p0) + cubicBezierP1(t, p1) + cubicBezierP2(t, p2) + cubicBezierP3(t, p3);
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
|
-
class CubicBezierCurve extends Curve {
|
|
1244
|
-
constructor(start = new Vector2(), startControl = new Vector2(), endControl = new Vector2(), end = new Vector2()) {
|
|
1245
|
-
super();
|
|
1246
|
-
this.start = start;
|
|
1247
|
-
this.startControl = startControl;
|
|
1248
|
-
this.endControl = endControl;
|
|
1249
|
-
this.end = end;
|
|
1250
|
-
}
|
|
1251
|
-
getPoint(t, output = new Vector2()) {
|
|
1252
|
-
const { start, startControl, endControl, end } = this;
|
|
1253
|
-
return output.set(
|
|
1254
|
-
cubicBezier(t, start.x, startControl.x, endControl.x, end.x),
|
|
1255
|
-
cubicBezier(t, start.y, startControl.y, endControl.y, end.y)
|
|
1256
|
-
);
|
|
1257
|
-
}
|
|
1258
|
-
getControlPoints() {
|
|
1259
|
-
return [
|
|
1260
|
-
this.start,
|
|
1261
|
-
this.startControl,
|
|
1262
|
-
this.endControl,
|
|
1263
|
-
this.end
|
|
1264
|
-
];
|
|
1265
|
-
}
|
|
1266
|
-
_solveQuadratic(a, b, c) {
|
|
1267
|
-
const discriminant = b * b - 4 * a * c;
|
|
1268
|
-
if (discriminant < 0)
|
|
1269
|
-
return [];
|
|
1270
|
-
const sqrtDiscriminant = Math.sqrt(discriminant);
|
|
1271
|
-
const t1 = (-b + sqrtDiscriminant) / (2 * a);
|
|
1272
|
-
const t2 = (-b - sqrtDiscriminant) / (2 * a);
|
|
1273
|
-
return [t1, t2].filter((t) => t >= 0 && t <= 1);
|
|
1274
|
-
}
|
|
1275
|
-
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1276
|
-
const p0 = this.start;
|
|
1277
|
-
const p1 = this.startControl;
|
|
1278
|
-
const p2 = this.endControl;
|
|
1279
|
-
const p3 = this.end;
|
|
1280
|
-
const dxRoots = this._solveQuadratic(
|
|
1281
|
-
3 * (p1.x - p0.x),
|
|
1282
|
-
6 * (p2.x - p1.x),
|
|
1283
|
-
3 * (p3.x - p2.x)
|
|
1284
|
-
);
|
|
1285
|
-
const dyRoots = this._solveQuadratic(
|
|
1286
|
-
3 * (p1.y - p0.y),
|
|
1287
|
-
6 * (p2.y - p1.y),
|
|
1288
|
-
3 * (p3.y - p2.y)
|
|
1289
|
-
);
|
|
1290
|
-
const tValues = [0, 1, ...dxRoots, ...dyRoots];
|
|
1291
|
-
const samplePoints = (tValues2, precision) => {
|
|
1292
|
-
for (const t of tValues2) {
|
|
1293
|
-
for (let i = 0; i <= precision; i++) {
|
|
1294
|
-
const delta = i / precision - 0.5;
|
|
1295
|
-
const refinedT = Math.min(1, Math.max(0, t + delta));
|
|
1296
|
-
const point = this.getPoint(refinedT);
|
|
1297
|
-
min.x = Math.min(min.x, point.x);
|
|
1298
|
-
min.y = Math.min(min.y, point.y);
|
|
1299
|
-
max.x = Math.max(max.x, point.x);
|
|
1300
|
-
max.y = Math.max(max.y, point.y);
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
};
|
|
1304
|
-
samplePoints(tValues, 10);
|
|
1305
|
-
return { min, max };
|
|
1306
|
-
}
|
|
1307
|
-
toCommands() {
|
|
1308
|
-
const { start, startControl, endControl, end } = this;
|
|
1309
|
-
return [
|
|
1310
|
-
{ type: "M", x: start.x, y: start.y },
|
|
1311
|
-
{ type: "C", x1: startControl.x, y1: startControl.y, x2: endControl.x, y2: endControl.y, x: end.x, y: end.y }
|
|
1312
|
-
];
|
|
1313
|
-
}
|
|
1314
|
-
drawTo(ctx) {
|
|
1315
|
-
const { start, startControl, endControl, end } = this;
|
|
1316
|
-
ctx.lineTo(start.x, start.y);
|
|
1317
|
-
ctx.bezierCurveTo(startControl.x, startControl.y, endControl.x, endControl.y, end.x, end.y);
|
|
1318
|
-
return this;
|
|
1319
|
-
}
|
|
1320
|
-
copy(source) {
|
|
1321
|
-
super.copy(source);
|
|
1322
|
-
this.start.copy(source.start);
|
|
1323
|
-
this.startControl.copy(source.startControl);
|
|
1324
|
-
this.endControl.copy(source.endControl);
|
|
1325
|
-
this.end.copy(source.end);
|
|
1326
|
-
return this;
|
|
1327
|
-
}
|
|
1328
|
-
}
|
|
1329
|
-
|
|
1330
1812
|
const tempTransform0$1 = new Matrix3();
|
|
1331
1813
|
const tempTransform1$1 = new Matrix3();
|
|
1332
1814
|
const tempTransform2$1 = new Matrix3();
|
|
1333
1815
|
const tempV2 = new Vector2();
|
|
1334
1816
|
class EllipseCurve extends Curve {
|
|
1335
|
-
constructor(
|
|
1817
|
+
constructor(cx = 0, cy = 0, rx = 1, ry = 1, rotation = 0, startAngle = 0, endAngle = Math.PI * 2, clockwise = false) {
|
|
1336
1818
|
super();
|
|
1337
|
-
this.center = center;
|
|
1338
|
-
this.radiusX = radiusX;
|
|
1339
|
-
this.radiusY = radiusY;
|
|
1340
1819
|
this.rotation = rotation;
|
|
1341
1820
|
this.startAngle = startAngle;
|
|
1342
1821
|
this.endAngle = endAngle;
|
|
1343
1822
|
this.clockwise = clockwise;
|
|
1823
|
+
this.center = new Vector2(cx, cy);
|
|
1824
|
+
this.radius = new Vector2(rx, ry);
|
|
1344
1825
|
}
|
|
1826
|
+
center;
|
|
1827
|
+
radius;
|
|
1345
1828
|
isClockwise() {
|
|
1346
1829
|
return this.clockwise;
|
|
1347
1830
|
}
|
|
@@ -1366,8 +1849,8 @@ class EllipseCurve extends Curve {
|
|
|
1366
1849
|
}
|
|
1367
1850
|
}
|
|
1368
1851
|
const angle = this.startAngle + t * deltaAngle;
|
|
1369
|
-
let _x = this.center.x + this.
|
|
1370
|
-
let _y = this.center.y + this.
|
|
1852
|
+
let _x = this.center.x + this.radius.x * Math.cos(angle);
|
|
1853
|
+
let _y = this.center.y + this.radius.y * Math.sin(angle);
|
|
1371
1854
|
if (this.rotation !== 0) {
|
|
1372
1855
|
const cos = Math.cos(this.rotation);
|
|
1373
1856
|
const sin = Math.sin(this.rotation);
|
|
@@ -1379,8 +1862,9 @@ class EllipseCurve extends Curve {
|
|
|
1379
1862
|
return output.set(_x, _y);
|
|
1380
1863
|
}
|
|
1381
1864
|
toCommands() {
|
|
1382
|
-
const { center,
|
|
1865
|
+
const { center, radius, startAngle, endAngle, clockwise, rotation } = this;
|
|
1383
1866
|
const { x: cx, y: cy } = center;
|
|
1867
|
+
const { x: rx, y: ry } = radius;
|
|
1384
1868
|
const startX = cx + rx * Math.cos(startAngle) * Math.cos(rotation) - ry * Math.sin(startAngle) * Math.sin(rotation);
|
|
1385
1869
|
const startY = cy + rx * Math.cos(startAngle) * Math.sin(rotation) + ry * Math.sin(startAngle) * Math.cos(rotation);
|
|
1386
1870
|
const angleDiff = Math.abs(startAngle - endAngle);
|
|
@@ -1406,12 +1890,12 @@ class EllipseCurve extends Curve {
|
|
|
1406
1890
|
}
|
|
1407
1891
|
}
|
|
1408
1892
|
drawTo(ctx) {
|
|
1409
|
-
const { center,
|
|
1893
|
+
const { center, radius, rotation, startAngle, endAngle, clockwise } = this;
|
|
1410
1894
|
ctx.ellipse(
|
|
1411
1895
|
center.x,
|
|
1412
1896
|
center.y,
|
|
1413
|
-
|
|
1414
|
-
|
|
1897
|
+
radius.x,
|
|
1898
|
+
radius.y,
|
|
1415
1899
|
rotation,
|
|
1416
1900
|
startAngle,
|
|
1417
1901
|
endAngle,
|
|
@@ -1431,11 +1915,119 @@ class EllipseCurve extends Curve {
|
|
|
1431
1915
|
}
|
|
1432
1916
|
return this;
|
|
1433
1917
|
}
|
|
1434
|
-
|
|
1918
|
+
getControlPointRefs() {
|
|
1435
1919
|
return [this.center];
|
|
1436
1920
|
}
|
|
1921
|
+
getAdaptivePointArray(output = []) {
|
|
1922
|
+
const i = output.length - 1;
|
|
1923
|
+
const x = this.center.x;
|
|
1924
|
+
const y = this.center.y;
|
|
1925
|
+
const rx = this.radius.x;
|
|
1926
|
+
const ry = this.radius.y;
|
|
1927
|
+
const dx = 0;
|
|
1928
|
+
const dy = 0;
|
|
1929
|
+
if (!(rx >= 0 && ry >= 0 && dx >= 0 && dy >= 0)) {
|
|
1930
|
+
return output;
|
|
1931
|
+
}
|
|
1932
|
+
const n = Math.ceil(2.3 * Math.sqrt(rx + ry));
|
|
1933
|
+
const m = n * 8 + (0) + (0);
|
|
1934
|
+
if (m === 0) {
|
|
1935
|
+
return output;
|
|
1936
|
+
}
|
|
1937
|
+
if (n === 0) {
|
|
1938
|
+
output[i] = output[i + 6] = x + dx;
|
|
1939
|
+
output[i + 1] = output[i + 3] = y + dy;
|
|
1940
|
+
output[i + 2] = output[i + 4] = x - dx;
|
|
1941
|
+
output[i + 5] = output[i + 7] = y - dy;
|
|
1942
|
+
return output;
|
|
1943
|
+
}
|
|
1944
|
+
let j1 = i;
|
|
1945
|
+
let j2 = n * 4 + (0) + 2 + i;
|
|
1946
|
+
let j3 = j2;
|
|
1947
|
+
let j4 = m + i;
|
|
1948
|
+
let x0 = dx + rx;
|
|
1949
|
+
let y0 = dy;
|
|
1950
|
+
let x1 = x + x0;
|
|
1951
|
+
let x2 = x - x0;
|
|
1952
|
+
let y1 = y + y0;
|
|
1953
|
+
output[j1++] = x1;
|
|
1954
|
+
output[j1++] = y1;
|
|
1955
|
+
output[--j2] = y1;
|
|
1956
|
+
output[--j2] = x2;
|
|
1957
|
+
for (let i2 = 1; i2 < n; i2++) {
|
|
1958
|
+
const a = Math.PI / 2 * (i2 / n);
|
|
1959
|
+
const x02 = dx + Math.cos(a) * rx;
|
|
1960
|
+
const y02 = dy + Math.sin(a) * ry;
|
|
1961
|
+
const x12 = x + x02;
|
|
1962
|
+
const x22 = x - x02;
|
|
1963
|
+
const y12 = y + y02;
|
|
1964
|
+
const y22 = y - y02;
|
|
1965
|
+
output[j1++] = x12;
|
|
1966
|
+
output[j1++] = y12;
|
|
1967
|
+
output[--j2] = y12;
|
|
1968
|
+
output[--j2] = x22;
|
|
1969
|
+
output[j3++] = x22;
|
|
1970
|
+
output[j3++] = y22;
|
|
1971
|
+
output[--j4] = y22;
|
|
1972
|
+
output[--j4] = x12;
|
|
1973
|
+
}
|
|
1974
|
+
x0 = dx;
|
|
1975
|
+
y0 = dy + ry;
|
|
1976
|
+
x1 = x + x0;
|
|
1977
|
+
x2 = x - x0;
|
|
1978
|
+
y1 = y + y0;
|
|
1979
|
+
const y2 = y - y0;
|
|
1980
|
+
output[j1++] = x1;
|
|
1981
|
+
output[j1++] = y1;
|
|
1982
|
+
output[--j4] = y2;
|
|
1983
|
+
output[--j4] = x1;
|
|
1984
|
+
return output;
|
|
1985
|
+
}
|
|
1986
|
+
fillTriangulate(options = {}) {
|
|
1987
|
+
let {
|
|
1988
|
+
vertices = [],
|
|
1989
|
+
indices = [],
|
|
1990
|
+
verticesStride = 2,
|
|
1991
|
+
verticesOffset = 0,
|
|
1992
|
+
indicesOffset = 0
|
|
1993
|
+
} = options;
|
|
1994
|
+
const points = this.getAdaptivePointArray();
|
|
1995
|
+
if (points.length === 0) {
|
|
1996
|
+
return { vertices, indices };
|
|
1997
|
+
}
|
|
1998
|
+
let centerX = 0;
|
|
1999
|
+
let centerY = 0;
|
|
2000
|
+
for (let i = 0; i < points.length; i += 2) {
|
|
2001
|
+
centerX += points[i];
|
|
2002
|
+
centerY += points[i + 1];
|
|
2003
|
+
}
|
|
2004
|
+
centerX /= points.length / 2;
|
|
2005
|
+
centerY /= points.length / 2;
|
|
2006
|
+
let count = verticesOffset;
|
|
2007
|
+
vertices[count * verticesStride] = centerX;
|
|
2008
|
+
vertices[count * verticesStride + 1] = centerY;
|
|
2009
|
+
const centerIndex = count++;
|
|
2010
|
+
for (let i = 0; i < points.length; i += 2) {
|
|
2011
|
+
vertices[count * verticesStride] = points[i];
|
|
2012
|
+
vertices[count * verticesStride + 1] = points[i + 1];
|
|
2013
|
+
if (i > 0) {
|
|
2014
|
+
indices[indicesOffset++] = count;
|
|
2015
|
+
indices[indicesOffset++] = centerIndex;
|
|
2016
|
+
indices[indicesOffset++] = count - 1;
|
|
2017
|
+
}
|
|
2018
|
+
count++;
|
|
2019
|
+
}
|
|
2020
|
+
indices[indicesOffset++] = centerIndex + 1;
|
|
2021
|
+
indices[indicesOffset++] = centerIndex;
|
|
2022
|
+
indices[indicesOffset++] = count - 1;
|
|
2023
|
+
return {
|
|
2024
|
+
vertices,
|
|
2025
|
+
indices
|
|
2026
|
+
};
|
|
2027
|
+
}
|
|
1437
2028
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1438
|
-
const { center,
|
|
2029
|
+
const { center, radius, rotation: theta } = this;
|
|
2030
|
+
const { x: rx, y: ry } = radius;
|
|
1439
2031
|
const { x: cx, y: cy } = center;
|
|
1440
2032
|
const cosTheta = Math.cos(theta);
|
|
1441
2033
|
const sinTheta = Math.sin(theta);
|
|
@@ -1455,8 +2047,8 @@ class EllipseCurve extends Curve {
|
|
|
1455
2047
|
super.copy(source);
|
|
1456
2048
|
this.center.x = source.center.x;
|
|
1457
2049
|
this.center.y = source.center.y;
|
|
1458
|
-
this.
|
|
1459
|
-
this.
|
|
2050
|
+
this.radius.x = source.radius.x;
|
|
2051
|
+
this.radius.y = source.radius.y;
|
|
1460
2052
|
this.startAngle = source.startAngle;
|
|
1461
2053
|
this.endAngle = source.endAngle;
|
|
1462
2054
|
this.clockwise = source.clockwise;
|
|
@@ -1465,8 +2057,8 @@ class EllipseCurve extends Curve {
|
|
|
1465
2057
|
}
|
|
1466
2058
|
}
|
|
1467
2059
|
function transfEllipseGeneric(curve, m) {
|
|
1468
|
-
const a = curve.
|
|
1469
|
-
const b = curve.
|
|
2060
|
+
const a = curve.radius.x;
|
|
2061
|
+
const b = curve.radius.y;
|
|
1470
2062
|
const cosTheta = Math.cos(curve.rotation);
|
|
1471
2063
|
const sinTheta = Math.sin(curve.rotation);
|
|
1472
2064
|
const v1 = new Vector2(a * cosTheta, a * sinTheta);
|
|
@@ -1491,8 +2083,8 @@ function transfEllipseGeneric(curve, m) {
|
|
|
1491
2083
|
const ed = eigenDecomposition(mQe[0], mQe[1], mQe[4]);
|
|
1492
2084
|
const rt1sqrt = Math.sqrt(ed.rt1);
|
|
1493
2085
|
const rt2sqrt = Math.sqrt(ed.rt2);
|
|
1494
|
-
curve.
|
|
1495
|
-
curve.
|
|
2086
|
+
curve.radius.x = 1 / rt1sqrt;
|
|
2087
|
+
curve.radius.y = 1 / rt2sqrt;
|
|
1496
2088
|
curve.rotation = Math.atan2(ed.sn, ed.cs);
|
|
1497
2089
|
const isFullEllipse = (curve.endAngle - curve.startAngle) % (2 * Math.PI) < Number.EPSILON;
|
|
1498
2090
|
if (!isFullEllipse) {
|
|
@@ -1533,8 +2125,8 @@ function transfEllipseGeneric(curve, m) {
|
|
|
1533
2125
|
function transfEllipseNoSkew(curve, m) {
|
|
1534
2126
|
const sx = getTransformScaleX(m);
|
|
1535
2127
|
const sy = getTransformScaleY(m);
|
|
1536
|
-
curve.
|
|
1537
|
-
curve.
|
|
2128
|
+
curve.radius.x *= sx;
|
|
2129
|
+
curve.radius.y *= sy;
|
|
1538
2130
|
const theta = sx > Number.EPSILON ? Math.atan2(m.elements[1], m.elements[0]) : Math.atan2(-m.elements[3], m.elements[4]);
|
|
1539
2131
|
curve.rotation += theta;
|
|
1540
2132
|
if (isTransformFlipped(m)) {
|
|
@@ -1596,357 +2188,455 @@ function eigenDecomposition(A, B, C) {
|
|
|
1596
2188
|
cs = 1 / Math.sqrt(1 + t * t);
|
|
1597
2189
|
sn = t * cs;
|
|
1598
2190
|
}
|
|
1599
|
-
if (df > 0) {
|
|
1600
|
-
t = cs;
|
|
1601
|
-
cs = -sn;
|
|
1602
|
-
sn = t;
|
|
2191
|
+
if (df > 0) {
|
|
2192
|
+
t = cs;
|
|
2193
|
+
cs = -sn;
|
|
2194
|
+
sn = t;
|
|
2195
|
+
}
|
|
2196
|
+
return { rt1, rt2, cs, sn };
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
class ArcCurve extends EllipseCurve {
|
|
2200
|
+
constructor(cx = 0, cy = 0, radius = 1, startAngle = 0, endAngle = Math.PI * 2, clockwise = false) {
|
|
2201
|
+
super(cx, cy, radius, radius, 0, startAngle, endAngle, clockwise);
|
|
2202
|
+
}
|
|
2203
|
+
getAdaptivePointArray(output = []) {
|
|
2204
|
+
const start = 0;
|
|
2205
|
+
const end = 1;
|
|
2206
|
+
const x = this.center.x;
|
|
2207
|
+
const y = this.center.y;
|
|
2208
|
+
const clockwise = this.clockwise;
|
|
2209
|
+
const radius = this.radius.x;
|
|
2210
|
+
let dist = Math.abs(start - end);
|
|
2211
|
+
if (clockwise && end > start) {
|
|
2212
|
+
dist = 2 * Math.PI - dist;
|
|
2213
|
+
}
|
|
2214
|
+
let steps = Math.max(6, Math.floor(6 * radius ** (1 / 3) * (dist / Math.PI)));
|
|
2215
|
+
steps = Math.max(steps, 3);
|
|
2216
|
+
let f = dist / steps;
|
|
2217
|
+
let t = start;
|
|
2218
|
+
f *= clockwise ? -1 : 1;
|
|
2219
|
+
for (let i = 0; i < steps + 1; i++) {
|
|
2220
|
+
const cs = Math.cos(t);
|
|
2221
|
+
const sn = Math.sin(t);
|
|
2222
|
+
const nx = x + cs * radius;
|
|
2223
|
+
const ny = y + sn * radius;
|
|
2224
|
+
output.push(nx, ny);
|
|
2225
|
+
t += f;
|
|
2226
|
+
}
|
|
2227
|
+
return output;
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
class CompositeCurve extends Curve {
|
|
2232
|
+
constructor(curves = []) {
|
|
2233
|
+
super();
|
|
2234
|
+
this.curves = curves;
|
|
2235
|
+
}
|
|
2236
|
+
addCurve(curve) {
|
|
2237
|
+
this.curves.push(curve);
|
|
2238
|
+
return this;
|
|
2239
|
+
}
|
|
2240
|
+
getPoint(t, output = new Vector2()) {
|
|
2241
|
+
const d = t * this.getLength();
|
|
2242
|
+
const lengths = this.getLengths();
|
|
2243
|
+
let i = 0;
|
|
2244
|
+
while (i < lengths.length) {
|
|
2245
|
+
if (lengths[i] >= d) {
|
|
2246
|
+
const diff = lengths[i] - d;
|
|
2247
|
+
const curve = this.curves[i];
|
|
2248
|
+
const length = curve.getLength();
|
|
2249
|
+
return curve.getPointAt(
|
|
2250
|
+
length === 0 ? 0 : 1 - diff / length,
|
|
2251
|
+
output
|
|
2252
|
+
);
|
|
2253
|
+
}
|
|
2254
|
+
i++;
|
|
2255
|
+
}
|
|
2256
|
+
return output;
|
|
2257
|
+
}
|
|
2258
|
+
updateLengths() {
|
|
2259
|
+
const arcLengths = [];
|
|
2260
|
+
for (let i = 0, sum = 0, len = this.curves.length; i < len; i++) {
|
|
2261
|
+
sum += this.curves[i].getLength();
|
|
2262
|
+
arcLengths.push(sum);
|
|
2263
|
+
}
|
|
2264
|
+
this._arcLengths = arcLengths;
|
|
2265
|
+
}
|
|
2266
|
+
getControlPointRefs() {
|
|
2267
|
+
return this.curves.flatMap((curve) => curve.getControlPointRefs());
|
|
2268
|
+
}
|
|
2269
|
+
getAdaptivePointArray(output = []) {
|
|
2270
|
+
let offset;
|
|
2271
|
+
this.curves.forEach((curve) => {
|
|
2272
|
+
curve.getAdaptivePointArray(output);
|
|
2273
|
+
if (offset) {
|
|
2274
|
+
if (output[offset - 1] === output[offset + 1] && output[offset] === output[offset + 2]) {
|
|
2275
|
+
output.splice(offset + 1, 2);
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
offset = output.length - 1;
|
|
2279
|
+
});
|
|
2280
|
+
return output;
|
|
2281
|
+
}
|
|
2282
|
+
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
2283
|
+
this.curves.forEach((curve) => curve.getMinMax(min, max));
|
|
2284
|
+
return { min, max };
|
|
2285
|
+
}
|
|
2286
|
+
getBoundingBox() {
|
|
2287
|
+
const { min, max } = this.getMinMax();
|
|
2288
|
+
return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
2289
|
+
}
|
|
2290
|
+
toCommands() {
|
|
2291
|
+
return this.curves.flatMap((curve) => curve.toCommands());
|
|
2292
|
+
}
|
|
2293
|
+
drawTo(ctx) {
|
|
2294
|
+
const point = this.curves[0]?.getPoint(0);
|
|
2295
|
+
if (point) {
|
|
2296
|
+
ctx.moveTo(point.x, point.y);
|
|
2297
|
+
}
|
|
2298
|
+
this.curves.forEach((curve) => curve.drawTo(ctx));
|
|
2299
|
+
return this;
|
|
1603
2300
|
}
|
|
1604
|
-
return { rt1, rt2, cs, sn };
|
|
1605
2301
|
}
|
|
1606
2302
|
|
|
1607
|
-
class
|
|
1608
|
-
|
|
2303
|
+
class CubicBezierCurve extends Curve {
|
|
2304
|
+
p1;
|
|
2305
|
+
cp1;
|
|
2306
|
+
cp2;
|
|
2307
|
+
p2;
|
|
2308
|
+
static from(p1, cp1, cp2, p2) {
|
|
2309
|
+
return new CubicBezierCurve(
|
|
2310
|
+
p1.x,
|
|
2311
|
+
p1.y,
|
|
2312
|
+
cp1.x,
|
|
2313
|
+
cp1.y,
|
|
2314
|
+
cp2.x,
|
|
2315
|
+
cp2.y,
|
|
2316
|
+
p2.x,
|
|
2317
|
+
p2.y
|
|
2318
|
+
);
|
|
2319
|
+
}
|
|
2320
|
+
constructor(p1x, p1y, cp1x, cp1y, cp2x, cp2y, p2x, p2y) {
|
|
1609
2321
|
super();
|
|
1610
|
-
this.
|
|
1611
|
-
this.
|
|
2322
|
+
this.p1 = new Vector2(p1x, p1y);
|
|
2323
|
+
this.cp1 = new Vector2(cp1x, cp1y);
|
|
2324
|
+
this.cp2 = new Vector2(cp2x, cp2y);
|
|
2325
|
+
this.p2 = new Vector2(p2x, p2y);
|
|
1612
2326
|
}
|
|
1613
2327
|
getPoint(t, output = new Vector2()) {
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
return output;
|
|
1620
|
-
}
|
|
1621
|
-
getPointAt(u, output = new Vector2()) {
|
|
1622
|
-
return this.getPoint(u, output);
|
|
2328
|
+
const { p1, cp1, cp2, p2 } = this;
|
|
2329
|
+
return output.set(
|
|
2330
|
+
cubicBezier(t, p1.x, cp1.x, cp2.x, p2.x),
|
|
2331
|
+
cubicBezier(t, p1.y, cp1.y, cp2.y, p2.y)
|
|
2332
|
+
);
|
|
1623
2333
|
}
|
|
1624
|
-
|
|
1625
|
-
return
|
|
2334
|
+
getAdaptivePointArray(output = []) {
|
|
2335
|
+
return getAdaptiveCubicBezierCurvePoints(
|
|
2336
|
+
this.p1.x,
|
|
2337
|
+
this.p1.y,
|
|
2338
|
+
this.cp1.x,
|
|
2339
|
+
this.cp1.y,
|
|
2340
|
+
this.cp2.x,
|
|
2341
|
+
this.cp2.y,
|
|
2342
|
+
this.p2.x,
|
|
2343
|
+
this.p2.y,
|
|
2344
|
+
0.5,
|
|
2345
|
+
output
|
|
2346
|
+
);
|
|
1626
2347
|
}
|
|
1627
|
-
|
|
1628
|
-
return this.
|
|
2348
|
+
getControlPointRefs() {
|
|
2349
|
+
return [this.p1, this.cp1, this.cp2, this.p2];
|
|
1629
2350
|
}
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
2351
|
+
_solveQuadratic(a, b, c) {
|
|
2352
|
+
const discriminant = b * b - 4 * a * c;
|
|
2353
|
+
if (discriminant < 0)
|
|
2354
|
+
return [];
|
|
2355
|
+
const sqrtDiscriminant = Math.sqrt(discriminant);
|
|
2356
|
+
const t1 = (-b + sqrtDiscriminant) / (2 * a);
|
|
2357
|
+
const t2 = (-b - sqrtDiscriminant) / (2 * a);
|
|
2358
|
+
return [t1, t2].filter((t) => t >= 0 && t <= 1);
|
|
1635
2359
|
}
|
|
1636
2360
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1637
|
-
const {
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
2361
|
+
const { p1, cp1, cp2, p2 } = this;
|
|
2362
|
+
const dxRoots = this._solveQuadratic(
|
|
2363
|
+
3 * (cp1.x - p1.x),
|
|
2364
|
+
6 * (cp2.x - cp1.x),
|
|
2365
|
+
3 * (p2.x - cp2.x)
|
|
2366
|
+
);
|
|
2367
|
+
const dyRoots = this._solveQuadratic(
|
|
2368
|
+
3 * (cp1.y - p1.y),
|
|
2369
|
+
6 * (cp2.y - cp1.y),
|
|
2370
|
+
3 * (p2.y - cp2.y)
|
|
2371
|
+
);
|
|
2372
|
+
const tValues = [0, 1, ...dxRoots, ...dyRoots];
|
|
2373
|
+
const samplePoints = (tValues2, precision) => {
|
|
2374
|
+
for (const t of tValues2) {
|
|
2375
|
+
for (let i = 0; i <= precision; i++) {
|
|
2376
|
+
const delta = i / precision - 0.5;
|
|
2377
|
+
const refinedT = Math.min(1, Math.max(0, t + delta));
|
|
2378
|
+
const point = this.getPoint(refinedT);
|
|
2379
|
+
min.x = Math.min(min.x, point.x);
|
|
2380
|
+
min.y = Math.min(min.y, point.y);
|
|
2381
|
+
max.x = Math.max(max.x, point.x);
|
|
2382
|
+
max.y = Math.max(max.y, point.y);
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
};
|
|
2386
|
+
samplePoints(tValues, 10);
|
|
1642
2387
|
return { min, max };
|
|
1643
2388
|
}
|
|
1644
2389
|
toCommands() {
|
|
1645
|
-
const {
|
|
2390
|
+
const { p1, cp1, cp2, p2 } = this;
|
|
1646
2391
|
return [
|
|
1647
|
-
{ type: "M", x:
|
|
1648
|
-
{ type: "
|
|
2392
|
+
{ type: "M", x: p1.x, y: p1.y },
|
|
2393
|
+
{ type: "C", x1: cp1.x, y1: cp1.y, x2: cp2.x, y2: cp2.y, x: p2.x, y: p2.y }
|
|
1649
2394
|
];
|
|
1650
2395
|
}
|
|
1651
2396
|
drawTo(ctx) {
|
|
1652
|
-
const {
|
|
1653
|
-
ctx.lineTo(
|
|
1654
|
-
ctx.
|
|
2397
|
+
const { p1, cp1, cp2, p2 } = this;
|
|
2398
|
+
ctx.lineTo(p1.x, p1.y);
|
|
2399
|
+
ctx.bezierCurveTo(cp1.x, cp1.y, cp2.x, cp2.y, p2.x, p2.y);
|
|
1655
2400
|
return this;
|
|
1656
2401
|
}
|
|
1657
2402
|
copy(source) {
|
|
1658
2403
|
super.copy(source);
|
|
1659
|
-
this.
|
|
1660
|
-
this.
|
|
2404
|
+
this.p1.copy(source.p1);
|
|
2405
|
+
this.cp1.copy(source.cp1);
|
|
2406
|
+
this.cp2.copy(source.cp2);
|
|
2407
|
+
this.p2.copy(source.p2);
|
|
1661
2408
|
return this;
|
|
1662
2409
|
}
|
|
1663
2410
|
}
|
|
1664
2411
|
|
|
1665
|
-
class
|
|
1666
|
-
|
|
2412
|
+
class LineCurve extends Curve {
|
|
2413
|
+
p1;
|
|
2414
|
+
p2;
|
|
2415
|
+
static from(p1, p2) {
|
|
2416
|
+
return new LineCurve(p1.x, p1.y, p2.x, p2.y);
|
|
2417
|
+
}
|
|
2418
|
+
constructor(p1x, p1y, p2x, p2y) {
|
|
1667
2419
|
super();
|
|
1668
|
-
this.
|
|
1669
|
-
this.
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
curveT = 0;
|
|
1675
|
-
update() {
|
|
1676
|
-
const { x, y } = this.center;
|
|
1677
|
-
const A = new Vector2(x + 0.5 * this.size, y - 0.5 * this.size);
|
|
1678
|
-
const t = new Vector2(x - 0.5 * this.size, y - 0.5 * this.size);
|
|
1679
|
-
const i = new Vector2(x, y + 0.5 * this.size);
|
|
1680
|
-
const curve1 = new CircleCurve(A, Math.SQRT1_2 * this.size, -0.25 * Math.PI, 0.75 * Math.PI);
|
|
1681
|
-
const curve5 = new CircleCurve(t, Math.SQRT1_2 * this.size, -0.75 * Math.PI, 0.25 * Math.PI);
|
|
1682
|
-
const curve3 = new CircleCurve(i, 0.5 * Math.SQRT1_2 * this.size, 0.75 * Math.PI, 1.25 * Math.PI);
|
|
1683
|
-
const e = new Vector2(x, y + this.size);
|
|
1684
|
-
const l = new Vector2(x + this.size, y);
|
|
1685
|
-
const c = new Vector2().lerpVectors(l, e, 0.75);
|
|
1686
|
-
const h = new Vector2(x - this.size, y);
|
|
1687
|
-
const a = new Vector2().lerpVectors(h, e, 0.75);
|
|
1688
|
-
const curve2 = new LineCurve(l, c);
|
|
1689
|
-
const curve4 = new LineCurve(a, h);
|
|
1690
|
-
this.curves = [curve1, curve2, curve3, curve4, curve5];
|
|
1691
|
-
return this;
|
|
1692
|
-
}
|
|
1693
|
-
getPoint(t) {
|
|
1694
|
-
return this.getCurve(t).getPoint(this.curveT);
|
|
1695
|
-
}
|
|
1696
|
-
getPointAt(t) {
|
|
1697
|
-
return this.getPoint(t);
|
|
1698
|
-
}
|
|
1699
|
-
getCurve(t) {
|
|
1700
|
-
let val = (t * (this.end - this.start) + this.start) % 1;
|
|
1701
|
-
val < 0 && (val += 1);
|
|
1702
|
-
val *= 9 * Math.PI / 8 + 1.5;
|
|
1703
|
-
let index;
|
|
1704
|
-
const PI_1_2 = 0.5 * Math.PI;
|
|
1705
|
-
if (val < PI_1_2) {
|
|
1706
|
-
index = 0;
|
|
1707
|
-
this.curveT = val / PI_1_2;
|
|
1708
|
-
} else if (val < PI_1_2 + 0.75) {
|
|
1709
|
-
index = 1;
|
|
1710
|
-
this.curveT = (val - PI_1_2) / 0.75;
|
|
1711
|
-
} else if (val < 5 * Math.PI / 8 + 0.75) {
|
|
1712
|
-
index = 2;
|
|
1713
|
-
this.curveT = (val - PI_1_2 - 0.75) / (Math.PI / 8);
|
|
1714
|
-
} else if (val < 5 * Math.PI / 8 + 1.5) {
|
|
1715
|
-
index = 3;
|
|
1716
|
-
this.curveT = (val - 5 * Math.PI / 8 - 0.75) / 0.75;
|
|
2420
|
+
this.p1 = new Vector2(p1x, p1y);
|
|
2421
|
+
this.p2 = new Vector2(p2x, p2y);
|
|
2422
|
+
}
|
|
2423
|
+
getPoint(t, output = new Vector2()) {
|
|
2424
|
+
if (t === 1) {
|
|
2425
|
+
output.copy(this.p2);
|
|
1717
2426
|
} else {
|
|
1718
|
-
|
|
1719
|
-
this.curveT = (val - 5 * Math.PI / 8 - 1.5) / PI_1_2;
|
|
2427
|
+
output.copy(this.p2).sub(this.p1).scale(t).add(this.p1);
|
|
1720
2428
|
}
|
|
1721
|
-
return
|
|
2429
|
+
return output;
|
|
2430
|
+
}
|
|
2431
|
+
getPointAt(u, output = new Vector2()) {
|
|
2432
|
+
return this.getPoint(u, output);
|
|
2433
|
+
}
|
|
2434
|
+
getTangent(_t, output = new Vector2()) {
|
|
2435
|
+
return output.subVectors(this.p2, this.p1).normalize();
|
|
1722
2436
|
}
|
|
1723
|
-
|
|
1724
|
-
return this.
|
|
2437
|
+
getTangentAt(u, output = new Vector2()) {
|
|
2438
|
+
return this.getTangent(u, output);
|
|
1725
2439
|
}
|
|
1726
|
-
|
|
1727
|
-
return this.
|
|
2440
|
+
getControlPointRefs() {
|
|
2441
|
+
return [this.p1, this.p2];
|
|
1728
2442
|
}
|
|
1729
|
-
|
|
1730
|
-
|
|
2443
|
+
getAdaptivePointArray(output = []) {
|
|
2444
|
+
output.push(
|
|
2445
|
+
this.p1.x,
|
|
2446
|
+
this.p1.y,
|
|
2447
|
+
this.p2.x,
|
|
2448
|
+
this.p2.y
|
|
2449
|
+
);
|
|
2450
|
+
return output;
|
|
1731
2451
|
}
|
|
1732
2452
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1733
|
-
|
|
2453
|
+
const { p1, p2 } = this;
|
|
2454
|
+
min.x = Math.min(min.x, p1.x, p2.x);
|
|
2455
|
+
min.y = Math.min(min.y, p1.y, p2.y);
|
|
2456
|
+
max.x = Math.max(max.x, p1.x, p2.x);
|
|
2457
|
+
max.y = Math.max(max.y, p1.y, p2.y);
|
|
1734
2458
|
return { min, max };
|
|
1735
2459
|
}
|
|
1736
2460
|
toCommands() {
|
|
1737
|
-
|
|
2461
|
+
const { p1, p2 } = this;
|
|
2462
|
+
return [
|
|
2463
|
+
{ type: "M", x: p1.x, y: p1.y },
|
|
2464
|
+
{ type: "L", x: p2.x, y: p2.y }
|
|
2465
|
+
];
|
|
1738
2466
|
}
|
|
1739
2467
|
drawTo(ctx) {
|
|
1740
|
-
|
|
2468
|
+
const { p1, p2 } = this;
|
|
2469
|
+
ctx.lineTo(p1.x, p1.y);
|
|
2470
|
+
ctx.lineTo(p2.x, p2.y);
|
|
2471
|
+
return this;
|
|
2472
|
+
}
|
|
2473
|
+
copy(source) {
|
|
2474
|
+
super.copy(source);
|
|
2475
|
+
this.p1.copy(source.p1);
|
|
2476
|
+
this.p2.copy(source.p2);
|
|
1741
2477
|
return this;
|
|
1742
2478
|
}
|
|
1743
2479
|
}
|
|
1744
2480
|
|
|
1745
|
-
class PloygonCurve extends
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
this.end = end;
|
|
1753
|
-
this.update();
|
|
1754
|
-
}
|
|
1755
|
-
curves = [];
|
|
1756
|
-
curveT = 0;
|
|
1757
|
-
points = [];
|
|
1758
|
-
update() {
|
|
1759
|
-
for (let i = 0; i < this.number; i++) {
|
|
1760
|
-
let radian = i * 2 * Math.PI / this.number;
|
|
1761
|
-
radian -= 0.5 * Math.PI;
|
|
1762
|
-
this.points.push(
|
|
2481
|
+
class PloygonCurve extends CompositeCurve {
|
|
2482
|
+
static equilateral(cx = 0, cy = 0, radius = 1, sideCount = 3) {
|
|
2483
|
+
const ploygon = new PloygonCurve();
|
|
2484
|
+
const points = [];
|
|
2485
|
+
for (let i = 0; i < sideCount; i++) {
|
|
2486
|
+
const radian = i * 2 * Math.PI / sideCount - 0.5 * Math.PI;
|
|
2487
|
+
points.push(
|
|
1763
2488
|
new Vector2(
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
).add(
|
|
2489
|
+
radius * Math.cos(radian),
|
|
2490
|
+
radius * Math.sin(radian)
|
|
2491
|
+
).add({ x: cx, y: cy })
|
|
1767
2492
|
);
|
|
1768
2493
|
}
|
|
1769
|
-
for (let i = 0; i <
|
|
1770
|
-
|
|
2494
|
+
for (let i = 0; i < sideCount; i++) {
|
|
2495
|
+
ploygon.curves.push(
|
|
2496
|
+
LineCurve.from(points[i], points[(i + 1) % sideCount])
|
|
2497
|
+
);
|
|
1771
2498
|
}
|
|
1772
|
-
return
|
|
1773
|
-
}
|
|
1774
|
-
getCurve(t) {
|
|
1775
|
-
let pos = (t * (this.end - this.start) + this.start) % 1;
|
|
1776
|
-
pos < 0 && (pos += 1);
|
|
1777
|
-
const v = pos * this.number;
|
|
1778
|
-
const index = Math.floor(v);
|
|
1779
|
-
this.curveT = v - index;
|
|
1780
|
-
return this.curves[index];
|
|
1781
|
-
}
|
|
1782
|
-
getPoint(t, output) {
|
|
1783
|
-
return this.getCurve(t).getPoint(this.curveT, output);
|
|
1784
|
-
}
|
|
1785
|
-
getPointAt(u, output) {
|
|
1786
|
-
return this.getPoint(u, output);
|
|
1787
|
-
}
|
|
1788
|
-
getTangent(t, output) {
|
|
1789
|
-
return this.getCurve(t).getTangent(this.curveT, output);
|
|
1790
|
-
}
|
|
1791
|
-
getNormal(t, output) {
|
|
1792
|
-
return this.getCurve(t).getNormal(this.curveT, output);
|
|
1793
|
-
}
|
|
1794
|
-
getControlPoints() {
|
|
1795
|
-
return this.curves.flatMap((curve) => curve.getControlPoints());
|
|
1796
|
-
}
|
|
1797
|
-
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1798
|
-
this.curves.forEach((curve) => curve.getMinMax(min, max));
|
|
1799
|
-
return { min, max };
|
|
1800
|
-
}
|
|
1801
|
-
toCommands() {
|
|
1802
|
-
return this.curves.flatMap((curve) => curve.toCommands());
|
|
1803
|
-
}
|
|
1804
|
-
drawTo(ctx) {
|
|
1805
|
-
this.curves.forEach((curve) => curve.drawTo(ctx));
|
|
1806
|
-
return this;
|
|
2499
|
+
return ploygon;
|
|
1807
2500
|
}
|
|
1808
2501
|
}
|
|
1809
2502
|
|
|
1810
2503
|
class QuadraticBezierCurve extends Curve {
|
|
1811
|
-
|
|
2504
|
+
p1;
|
|
2505
|
+
cp;
|
|
2506
|
+
p2;
|
|
2507
|
+
static from(p1, cp, p2) {
|
|
2508
|
+
return new QuadraticBezierCurve(
|
|
2509
|
+
p1.x,
|
|
2510
|
+
p1.y,
|
|
2511
|
+
cp.x,
|
|
2512
|
+
cp.y,
|
|
2513
|
+
p2.x,
|
|
2514
|
+
p2.y
|
|
2515
|
+
);
|
|
2516
|
+
}
|
|
2517
|
+
constructor(p1x, p1y, cpx, cpy, p2x, p2y) {
|
|
1812
2518
|
super();
|
|
1813
|
-
this.
|
|
1814
|
-
this.
|
|
1815
|
-
this.
|
|
2519
|
+
this.p1 = new Vector2(p1x, p1y);
|
|
2520
|
+
this.cp = new Vector2(cpx, cpy);
|
|
2521
|
+
this.p2 = new Vector2(p2x, p2y);
|
|
1816
2522
|
}
|
|
1817
2523
|
getPoint(t, output = new Vector2()) {
|
|
1818
|
-
const {
|
|
2524
|
+
const { p1, cp, p2 } = this;
|
|
1819
2525
|
output.set(
|
|
1820
|
-
quadraticBezier(t,
|
|
1821
|
-
quadraticBezier(t,
|
|
2526
|
+
quadraticBezier(t, p1.x, cp.x, p2.x),
|
|
2527
|
+
quadraticBezier(t, p1.y, cp.y, p2.y)
|
|
1822
2528
|
);
|
|
1823
2529
|
return output;
|
|
1824
2530
|
}
|
|
1825
|
-
|
|
1826
|
-
return [
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
2531
|
+
getControlPointRefs() {
|
|
2532
|
+
return [this.p1, this.cp, this.p2];
|
|
2533
|
+
}
|
|
2534
|
+
getAdaptivePointArray(output = []) {
|
|
2535
|
+
return getAdaptiveQuadraticBezierCurvePoints(
|
|
2536
|
+
this.p1.x,
|
|
2537
|
+
this.p1.y,
|
|
2538
|
+
this.cp.x,
|
|
2539
|
+
this.cp.y,
|
|
2540
|
+
this.p2.x,
|
|
2541
|
+
this.p2.y,
|
|
2542
|
+
0.5,
|
|
2543
|
+
output
|
|
2544
|
+
);
|
|
1831
2545
|
}
|
|
1832
2546
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1833
|
-
const {
|
|
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,
|
|
2547
|
+
const { p1, cp, p2 } = this;
|
|
2548
|
+
const x1 = 0.5 * (p1.x + cp.x);
|
|
2549
|
+
const y1 = 0.5 * (p1.y + cp.y);
|
|
2550
|
+
const x2 = 0.5 * (p1.x + p2.x);
|
|
2551
|
+
const y2 = 0.5 * (p1.y + p2.y);
|
|
2552
|
+
min.x = Math.min(min.x, p1.x, p2.x, x1, x2);
|
|
2553
|
+
min.y = Math.min(min.y, p1.y, p2.y, y1, y2);
|
|
2554
|
+
max.x = Math.max(max.x, p1.x, p2.x, x1, x2);
|
|
2555
|
+
max.y = Math.max(max.y, p1.y, p2.y, y1, y2);
|
|
1842
2556
|
return { min, max };
|
|
1843
2557
|
}
|
|
1844
2558
|
toCommands() {
|
|
1845
|
-
const {
|
|
2559
|
+
const { p1, cp, p2 } = this;
|
|
1846
2560
|
return [
|
|
1847
|
-
{ type: "M", x:
|
|
1848
|
-
{ type: "Q", x1:
|
|
2561
|
+
{ type: "M", x: p1.x, y: p1.y },
|
|
2562
|
+
{ type: "Q", x1: cp.x, y1: cp.y, x: p2.x, y: p2.y }
|
|
1849
2563
|
];
|
|
1850
2564
|
}
|
|
1851
2565
|
drawTo(ctx) {
|
|
1852
|
-
const {
|
|
1853
|
-
ctx.lineTo(
|
|
1854
|
-
ctx.quadraticCurveTo(
|
|
2566
|
+
const { p1, cp, p2 } = this;
|
|
2567
|
+
ctx.lineTo(p1.x, p1.y);
|
|
2568
|
+
ctx.quadraticCurveTo(cp.x, cp.y, p2.x, p2.y);
|
|
1855
2569
|
return this;
|
|
1856
2570
|
}
|
|
1857
2571
|
copy(source) {
|
|
1858
2572
|
super.copy(source);
|
|
1859
|
-
this.
|
|
1860
|
-
this.
|
|
1861
|
-
this.
|
|
2573
|
+
this.p1.copy(source.p1);
|
|
2574
|
+
this.cp.copy(source.cp);
|
|
2575
|
+
this.p2.copy(source.p2);
|
|
1862
2576
|
return this;
|
|
1863
2577
|
}
|
|
1864
2578
|
}
|
|
1865
2579
|
|
|
1866
|
-
class
|
|
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;
|
|
2580
|
+
class RectangleCurve extends PloygonCurve {
|
|
2581
|
+
constructor(x = 0, y = 0, width = 0, height = 0) {
|
|
1894
2582
|
const points = [
|
|
1895
|
-
new Vector2(x
|
|
1896
|
-
new Vector2(x +
|
|
1897
|
-
new Vector2(x +
|
|
1898
|
-
new Vector2(x
|
|
2583
|
+
new Vector2(x, y),
|
|
2584
|
+
new Vector2(x + width, y),
|
|
2585
|
+
new Vector2(x + width, y + height),
|
|
2586
|
+
new Vector2(x, y + height)
|
|
1899
2587
|
];
|
|
1900
|
-
|
|
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());
|
|
1939
|
-
}
|
|
1940
|
-
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1941
|
-
this.curves.forEach((curve) => curve.getMinMax(min, max));
|
|
1942
|
-
return { min, max };
|
|
1943
|
-
}
|
|
1944
|
-
toCommands() {
|
|
1945
|
-
return this.curves.flatMap((curve) => curve.toCommands());
|
|
2588
|
+
super([
|
|
2589
|
+
LineCurve.from(points[0], points[1]),
|
|
2590
|
+
LineCurve.from(points[1], points[2]),
|
|
2591
|
+
LineCurve.from(points[2], points[3]),
|
|
2592
|
+
LineCurve.from(points[3], points[0])
|
|
2593
|
+
]);
|
|
2594
|
+
this.x = x;
|
|
2595
|
+
this.y = y;
|
|
2596
|
+
this.width = width;
|
|
2597
|
+
this.height = height;
|
|
1946
2598
|
}
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
2599
|
+
fillTriangulate(options = {}) {
|
|
2600
|
+
let {
|
|
2601
|
+
vertices = [],
|
|
2602
|
+
indices = [],
|
|
2603
|
+
verticesStride = 2,
|
|
2604
|
+
verticesOffset = 0,
|
|
2605
|
+
indicesOffset = 0
|
|
2606
|
+
} = options;
|
|
2607
|
+
const { x, y, width, height } = this;
|
|
2608
|
+
const points = [
|
|
2609
|
+
x,
|
|
2610
|
+
y,
|
|
2611
|
+
x + width,
|
|
2612
|
+
y,
|
|
2613
|
+
x + width,
|
|
2614
|
+
y + height,
|
|
2615
|
+
x,
|
|
2616
|
+
y + height
|
|
2617
|
+
];
|
|
2618
|
+
let count = 0;
|
|
2619
|
+
verticesOffset *= verticesStride;
|
|
2620
|
+
vertices[verticesOffset + count] = points[0];
|
|
2621
|
+
vertices[verticesOffset + count + 1] = points[1];
|
|
2622
|
+
count += verticesStride;
|
|
2623
|
+
vertices[verticesOffset + count] = points[2];
|
|
2624
|
+
vertices[verticesOffset + count + 1] = points[3];
|
|
2625
|
+
count += verticesStride;
|
|
2626
|
+
vertices[verticesOffset + count] = points[6];
|
|
2627
|
+
vertices[verticesOffset + count + 1] = points[7];
|
|
2628
|
+
count += verticesStride;
|
|
2629
|
+
vertices[verticesOffset + count] = points[4];
|
|
2630
|
+
vertices[verticesOffset + count + 1] = points[5];
|
|
2631
|
+
count += verticesStride;
|
|
2632
|
+
const verticesIndex = verticesOffset / verticesStride;
|
|
2633
|
+
indices[indicesOffset++] = verticesIndex;
|
|
2634
|
+
indices[indicesOffset++] = verticesIndex + 1;
|
|
2635
|
+
indices[indicesOffset++] = verticesIndex + 2;
|
|
2636
|
+
indices[indicesOffset++] = verticesIndex + 1;
|
|
2637
|
+
indices[indicesOffset++] = verticesIndex + 3;
|
|
2638
|
+
indices[indicesOffset++] = verticesIndex + 2;
|
|
2639
|
+
return { vertices, indices };
|
|
1950
2640
|
}
|
|
1951
2641
|
}
|
|
1952
2642
|
|
|
@@ -1970,7 +2660,7 @@ class SplineCurve extends Curve {
|
|
|
1970
2660
|
);
|
|
1971
2661
|
return output;
|
|
1972
2662
|
}
|
|
1973
|
-
|
|
2663
|
+
getControlPointRefs() {
|
|
1974
2664
|
return this.points;
|
|
1975
2665
|
}
|
|
1976
2666
|
copy(source) {
|
|
@@ -1983,22 +2673,16 @@ class SplineCurve extends Curve {
|
|
|
1983
2673
|
}
|
|
1984
2674
|
}
|
|
1985
2675
|
|
|
1986
|
-
class CurvePath extends
|
|
1987
|
-
curves = [];
|
|
2676
|
+
class CurvePath extends CompositeCurve {
|
|
1988
2677
|
startPoint;
|
|
1989
2678
|
currentPoint;
|
|
1990
2679
|
autoClose = false;
|
|
1991
|
-
_cacheLengths = [];
|
|
1992
2680
|
constructor(points) {
|
|
1993
2681
|
super();
|
|
1994
2682
|
if (points) {
|
|
1995
2683
|
this.addPoints(points);
|
|
1996
2684
|
}
|
|
1997
2685
|
}
|
|
1998
|
-
addCurve(curve) {
|
|
1999
|
-
this.curves.push(curve);
|
|
2000
|
-
return this;
|
|
2001
|
-
}
|
|
2002
2686
|
addPoints(points) {
|
|
2003
2687
|
this.moveTo(points[0].x, points[0].y);
|
|
2004
2688
|
for (let i = 1, len = points.length; i < len; i++) {
|
|
@@ -2008,82 +2692,26 @@ class CurvePath extends Curve {
|
|
|
2008
2692
|
return this;
|
|
2009
2693
|
}
|
|
2010
2694
|
addCommands(commands) {
|
|
2011
|
-
|
|
2695
|
+
svgPathCommandsAddToPath2D(commands, this);
|
|
2012
2696
|
return this;
|
|
2013
2697
|
}
|
|
2014
2698
|
addData(data) {
|
|
2015
|
-
this.addCommands(
|
|
2699
|
+
this.addCommands(svgPathDataToCommands(data));
|
|
2016
2700
|
return this;
|
|
2017
2701
|
}
|
|
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++;
|
|
2702
|
+
getUnevenPointArray(count = 40, output = []) {
|
|
2703
|
+
super.getUnevenPointArray(count, output);
|
|
2704
|
+
if (this.autoClose && output.length >= 4 && (output[0] !== output[output.length - 2] && output[1] !== output[output.length - 1])) {
|
|
2705
|
+
output.push(output[0], output[1]);
|
|
2030
2706
|
}
|
|
2031
2707
|
return output;
|
|
2032
2708
|
}
|
|
2033
|
-
|
|
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
|
-
}
|
|
2709
|
+
getSpacedPointArray(count = 40, output = []) {
|
|
2710
|
+
super.getSpacedPointArray(count, output);
|
|
2711
|
+
if (this.autoClose && output.length >= 4 && (output[0] !== output[output.length - 2] && output[1] !== output[output.length - 1])) {
|
|
2712
|
+
output.push(output[0], output[1]);
|
|
2082
2713
|
}
|
|
2083
|
-
|
|
2084
|
-
points.push(points[0]);
|
|
2085
|
-
}
|
|
2086
|
-
return points;
|
|
2714
|
+
return output;
|
|
2087
2715
|
}
|
|
2088
2716
|
_setCurrentPoint(point) {
|
|
2089
2717
|
this.currentPoint = new Vector2(point.x, point.y);
|
|
@@ -2092,12 +2720,21 @@ class CurvePath extends Curve {
|
|
|
2092
2720
|
}
|
|
2093
2721
|
return this;
|
|
2094
2722
|
}
|
|
2723
|
+
_connetLineTo(curve) {
|
|
2724
|
+
if (this.curves.length > 0) {
|
|
2725
|
+
const first = curve.getPoint(0);
|
|
2726
|
+
if (!this.currentPoint || !first.equals(this.currentPoint)) {
|
|
2727
|
+
this.lineTo(first.x, first.y);
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
return this;
|
|
2731
|
+
}
|
|
2095
2732
|
closePath() {
|
|
2096
2733
|
const start = this.startPoint;
|
|
2097
2734
|
if (start) {
|
|
2098
2735
|
const end = this.currentPoint;
|
|
2099
2736
|
if (end && !start.equals(end)) {
|
|
2100
|
-
this.curves.push(
|
|
2737
|
+
this.curves.push(LineCurve.from(end, start));
|
|
2101
2738
|
end.copy(start);
|
|
2102
2739
|
}
|
|
2103
2740
|
this.startPoint = void 0;
|
|
@@ -2114,8 +2751,10 @@ class CurvePath extends Curve {
|
|
|
2114
2751
|
if (!start?.equals({ x, y })) {
|
|
2115
2752
|
this.curves.push(
|
|
2116
2753
|
new LineCurve(
|
|
2117
|
-
start?.
|
|
2118
|
-
|
|
2754
|
+
start?.x ?? 0,
|
|
2755
|
+
start?.y ?? 0,
|
|
2756
|
+
x,
|
|
2757
|
+
y
|
|
2119
2758
|
)
|
|
2120
2759
|
);
|
|
2121
2760
|
}
|
|
@@ -2127,10 +2766,14 @@ class CurvePath extends Curve {
|
|
|
2127
2766
|
if (!start?.equals({ x, y })) {
|
|
2128
2767
|
this.curves.push(
|
|
2129
2768
|
new CubicBezierCurve(
|
|
2130
|
-
start?.
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2769
|
+
start?.x ?? 0,
|
|
2770
|
+
start?.y ?? 0,
|
|
2771
|
+
cp1x,
|
|
2772
|
+
cp1y,
|
|
2773
|
+
cp2x,
|
|
2774
|
+
cp2y,
|
|
2775
|
+
x,
|
|
2776
|
+
y
|
|
2134
2777
|
)
|
|
2135
2778
|
);
|
|
2136
2779
|
}
|
|
@@ -2142,9 +2785,12 @@ class CurvePath extends Curve {
|
|
|
2142
2785
|
if (!start?.equals({ x, y })) {
|
|
2143
2786
|
this.curves.push(
|
|
2144
2787
|
new QuadraticBezierCurve(
|
|
2145
|
-
start?.
|
|
2146
|
-
|
|
2147
|
-
|
|
2788
|
+
start?.x ?? 0,
|
|
2789
|
+
start?.y ?? 0,
|
|
2790
|
+
cpx,
|
|
2791
|
+
cpy,
|
|
2792
|
+
x,
|
|
2793
|
+
y
|
|
2148
2794
|
)
|
|
2149
2795
|
);
|
|
2150
2796
|
}
|
|
@@ -2152,12 +2798,23 @@ class CurvePath extends Curve {
|
|
|
2152
2798
|
return this;
|
|
2153
2799
|
}
|
|
2154
2800
|
arc(x, y, radius, startAngle, endAngle, counterclockwise) {
|
|
2155
|
-
|
|
2801
|
+
const curve = new ArcCurve(
|
|
2802
|
+
x,
|
|
2803
|
+
y,
|
|
2804
|
+
radius,
|
|
2805
|
+
startAngle,
|
|
2806
|
+
endAngle,
|
|
2807
|
+
!counterclockwise
|
|
2808
|
+
);
|
|
2809
|
+
this._connetLineTo(curve);
|
|
2810
|
+
this.curves.push(curve);
|
|
2811
|
+
this._setCurrentPoint(curve.getPoint(1));
|
|
2156
2812
|
return this;
|
|
2157
2813
|
}
|
|
2158
2814
|
relativeArc(x, y, radius, startAngle, endAngle, counterclockwise) {
|
|
2159
|
-
|
|
2160
|
-
|
|
2815
|
+
x += this.currentPoint?.x ?? 0;
|
|
2816
|
+
y += this.currentPoint?.y ?? 0;
|
|
2817
|
+
this.arc(x, y, radius, startAngle, endAngle, counterclockwise);
|
|
2161
2818
|
return this;
|
|
2162
2819
|
}
|
|
2163
2820
|
// TODO
|
|
@@ -2168,7 +2825,8 @@ class CurvePath extends Curve {
|
|
|
2168
2825
|
}
|
|
2169
2826
|
ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise = true) {
|
|
2170
2827
|
const curve = new EllipseCurve(
|
|
2171
|
-
|
|
2828
|
+
x,
|
|
2829
|
+
y,
|
|
2172
2830
|
radiusX,
|
|
2173
2831
|
radiusY,
|
|
2174
2832
|
rotation,
|
|
@@ -2176,29 +2834,21 @@ class CurvePath extends Curve {
|
|
|
2176
2834
|
endAngle,
|
|
2177
2835
|
!counterclockwise
|
|
2178
2836
|
);
|
|
2179
|
-
|
|
2180
|
-
const first = curve.getPoint(0);
|
|
2181
|
-
if (!this.currentPoint || !first.equals(this.currentPoint)) {
|
|
2182
|
-
this.lineTo(first.x, first.y);
|
|
2183
|
-
}
|
|
2184
|
-
}
|
|
2837
|
+
this._connetLineTo(curve);
|
|
2185
2838
|
this.curves.push(curve);
|
|
2186
2839
|
this._setCurrentPoint(curve.getPoint(1));
|
|
2187
2840
|
return this;
|
|
2188
2841
|
}
|
|
2189
2842
|
relativeEllipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) {
|
|
2190
|
-
|
|
2191
|
-
|
|
2843
|
+
x += this.currentPoint?.x ?? 0;
|
|
2844
|
+
y += this.currentPoint?.y ?? 0;
|
|
2845
|
+
this.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise);
|
|
2192
2846
|
return this;
|
|
2193
2847
|
}
|
|
2194
2848
|
rect(x, y, w, h) {
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
w / 2,
|
|
2199
|
-
w / h
|
|
2200
|
-
)
|
|
2201
|
-
);
|
|
2849
|
+
const curve = new RectangleCurve(x, y, w, h);
|
|
2850
|
+
this._connetLineTo(curve);
|
|
2851
|
+
this.curves.push(curve);
|
|
2202
2852
|
this._setCurrentPoint({ x, y });
|
|
2203
2853
|
return this;
|
|
2204
2854
|
}
|
|
@@ -2208,17 +2858,6 @@ class CurvePath extends Curve {
|
|
|
2208
2858
|
this._setCurrentPoint(points[points.length - 1]);
|
|
2209
2859
|
return this;
|
|
2210
2860
|
}
|
|
2211
|
-
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
2212
|
-
this.curves.forEach((curve) => curve.getMinMax(min, max));
|
|
2213
|
-
return { min, max };
|
|
2214
|
-
}
|
|
2215
|
-
getBoundingBox() {
|
|
2216
|
-
const { min, max } = this.getMinMax();
|
|
2217
|
-
return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
2218
|
-
}
|
|
2219
|
-
toCommands() {
|
|
2220
|
-
return this.curves.flatMap((curve) => curve.toCommands());
|
|
2221
|
-
}
|
|
2222
2861
|
drawTo(ctx) {
|
|
2223
2862
|
const point = this.curves[0]?.getPoint(0);
|
|
2224
2863
|
if (point) {
|
|
@@ -2269,20 +2908,22 @@ function getIntersectionPoint(p1, p2, q1, q2) {
|
|
|
2269
2908
|
);
|
|
2270
2909
|
}
|
|
2271
2910
|
|
|
2272
|
-
class Path2D {
|
|
2273
|
-
|
|
2274
|
-
paths = [this.currentPath];
|
|
2911
|
+
class Path2D extends CompositeCurve {
|
|
2912
|
+
currentCurve = new CurvePath();
|
|
2275
2913
|
style;
|
|
2276
2914
|
get startPoint() {
|
|
2277
|
-
return this.
|
|
2915
|
+
return this.currentCurve.startPoint;
|
|
2278
2916
|
}
|
|
2279
2917
|
get currentPoint() {
|
|
2280
|
-
return this.
|
|
2918
|
+
return this.currentCurve.currentPoint;
|
|
2281
2919
|
}
|
|
2282
2920
|
get strokeWidth() {
|
|
2283
2921
|
return this.style.strokeWidth ?? ((this.style.stroke ?? "none") === "none" ? 0 : 1);
|
|
2284
2922
|
}
|
|
2285
2923
|
constructor(path, style = {}) {
|
|
2924
|
+
super();
|
|
2925
|
+
this.curves.push(this.currentCurve);
|
|
2926
|
+
this.style = style;
|
|
2286
2927
|
if (path) {
|
|
2287
2928
|
if (path instanceof Path2D) {
|
|
2288
2929
|
this.addPath(path);
|
|
@@ -2292,99 +2933,90 @@ class Path2D {
|
|
|
2292
2933
|
this.addData(path);
|
|
2293
2934
|
}
|
|
2294
2935
|
}
|
|
2295
|
-
this.style = style;
|
|
2296
2936
|
}
|
|
2297
2937
|
addPath(path) {
|
|
2298
2938
|
if (path instanceof Path2D) {
|
|
2299
|
-
this.
|
|
2939
|
+
this.curves.push(...path.curves.map((v) => v.clone()));
|
|
2300
2940
|
} else {
|
|
2301
|
-
this.
|
|
2941
|
+
this.curves.push(path);
|
|
2302
2942
|
}
|
|
2303
2943
|
return this;
|
|
2304
2944
|
}
|
|
2305
2945
|
closePath() {
|
|
2306
2946
|
const startPoint = this.startPoint;
|
|
2307
2947
|
if (startPoint) {
|
|
2308
|
-
this.
|
|
2309
|
-
if (this.
|
|
2310
|
-
this.
|
|
2311
|
-
this.
|
|
2948
|
+
this.currentCurve.closePath();
|
|
2949
|
+
if (this.currentCurve.curves.length > 0) {
|
|
2950
|
+
this.currentCurve = new CurvePath().moveTo(startPoint.x, startPoint.y);
|
|
2951
|
+
this.curves.push(this.currentCurve);
|
|
2312
2952
|
}
|
|
2313
2953
|
}
|
|
2314
2954
|
return this;
|
|
2315
2955
|
}
|
|
2316
2956
|
moveTo(x, y) {
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
this.
|
|
2321
|
-
this.paths.push(this.currentPath);
|
|
2322
|
-
} else {
|
|
2323
|
-
this.currentPath.moveTo(x, y);
|
|
2957
|
+
if (!this.currentCurve.currentPoint?.equals({ x, y })) {
|
|
2958
|
+
if (this.currentCurve.curves.length) {
|
|
2959
|
+
this.currentCurve = new CurvePath();
|
|
2960
|
+
this.curves.push(this.currentCurve);
|
|
2324
2961
|
}
|
|
2962
|
+
this.currentCurve.moveTo(x, y);
|
|
2325
2963
|
}
|
|
2326
2964
|
return this;
|
|
2327
2965
|
}
|
|
2328
2966
|
lineTo(x, y) {
|
|
2329
|
-
this.
|
|
2967
|
+
this.currentCurve.lineTo(x, y);
|
|
2330
2968
|
return this;
|
|
2331
2969
|
}
|
|
2332
2970
|
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
|
|
2333
|
-
this.
|
|
2971
|
+
this.currentCurve.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
|
|
2334
2972
|
return this;
|
|
2335
2973
|
}
|
|
2336
2974
|
quadraticCurveTo(cpx, cpy, x, y) {
|
|
2337
|
-
this.
|
|
2975
|
+
this.currentCurve.quadraticCurveTo(cpx, cpy, x, y);
|
|
2338
2976
|
return this;
|
|
2339
2977
|
}
|
|
2340
2978
|
arc(x, y, radius, startAngle, endAngle, counterclockwise) {
|
|
2341
|
-
this.
|
|
2979
|
+
this.currentCurve.arc(x, y, radius, startAngle, endAngle, counterclockwise);
|
|
2342
2980
|
return this;
|
|
2343
2981
|
}
|
|
2344
2982
|
arcTo(x1, y1, x2, y2, radius) {
|
|
2345
|
-
this.
|
|
2983
|
+
this.currentCurve.arcTo(x1, y1, x2, y2, radius);
|
|
2346
2984
|
return this;
|
|
2347
2985
|
}
|
|
2348
2986
|
ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) {
|
|
2349
|
-
this.
|
|
2987
|
+
this.currentCurve.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise);
|
|
2350
2988
|
return this;
|
|
2351
2989
|
}
|
|
2352
2990
|
rect(x, y, w, h) {
|
|
2353
|
-
this.
|
|
2991
|
+
this.currentCurve.rect(x, y, w, h);
|
|
2354
2992
|
return this;
|
|
2355
2993
|
}
|
|
2356
2994
|
addCommands(commands) {
|
|
2357
|
-
|
|
2995
|
+
svgPathCommandsAddToPath2D(commands, this);
|
|
2358
2996
|
return this;
|
|
2359
2997
|
}
|
|
2360
2998
|
addData(data) {
|
|
2361
|
-
this.addCommands(
|
|
2999
|
+
this.addCommands(svgPathDataToCommands(data));
|
|
2362
3000
|
return this;
|
|
2363
3001
|
}
|
|
2364
3002
|
splineThru(points) {
|
|
2365
|
-
this.
|
|
3003
|
+
this.currentCurve.splineThru(points);
|
|
2366
3004
|
return this;
|
|
2367
3005
|
}
|
|
2368
|
-
getControlPoints() {
|
|
2369
|
-
return this.paths.flatMap((path) => path.getControlPoints());
|
|
2370
|
-
}
|
|
2371
|
-
getCurves() {
|
|
2372
|
-
return this.paths.flatMap((path) => path.curves);
|
|
2373
|
-
}
|
|
2374
3006
|
scale(sx, sy = sx, target = { x: 0, y: 0 }) {
|
|
2375
|
-
this.
|
|
3007
|
+
this.getControlPointRefs().forEach((point) => {
|
|
2376
3008
|
point.scale(sx, sy, target);
|
|
2377
3009
|
});
|
|
2378
3010
|
return this;
|
|
2379
3011
|
}
|
|
2380
3012
|
skew(ax, ay = 0, target = { x: 0, y: 0 }) {
|
|
2381
|
-
this.
|
|
3013
|
+
this.getControlPointRefs().forEach((point) => {
|
|
2382
3014
|
point.skew(ax, ay, target);
|
|
2383
3015
|
});
|
|
2384
3016
|
return this;
|
|
2385
3017
|
}
|
|
2386
3018
|
rotate(a, target = { x: 0, y: 0 }) {
|
|
2387
|
-
this.
|
|
3019
|
+
this.getControlPointRefs().forEach((point) => {
|
|
2388
3020
|
point.rotate(a, target);
|
|
2389
3021
|
});
|
|
2390
3022
|
return this;
|
|
@@ -2393,12 +3025,14 @@ class Path2D {
|
|
|
2393
3025
|
if (b === 0) {
|
|
2394
3026
|
return this;
|
|
2395
3027
|
}
|
|
2396
|
-
const curves = this.
|
|
3028
|
+
const curves = this.curves;
|
|
2397
3029
|
const _list = [];
|
|
2398
3030
|
const _isClockwise = [];
|
|
2399
3031
|
const _points = [];
|
|
2400
3032
|
curves.forEach((curve, index) => {
|
|
2401
|
-
|
|
3033
|
+
if (!curve.getLength())
|
|
3034
|
+
return;
|
|
3035
|
+
const points = curve.getControlPointRefs();
|
|
2402
3036
|
const isClockwise = curve.isClockwise();
|
|
2403
3037
|
_points[index] = points;
|
|
2404
3038
|
_isClockwise[index] = isClockwise;
|
|
@@ -2420,6 +3054,8 @@ class Path2D {
|
|
|
2420
3054
|
});
|
|
2421
3055
|
});
|
|
2422
3056
|
curves.forEach((curve, index) => {
|
|
3057
|
+
if (!curve.getLength())
|
|
3058
|
+
return;
|
|
2423
3059
|
const isClockwise = _isClockwise[index];
|
|
2424
3060
|
const points = _points[index];
|
|
2425
3061
|
points.forEach((point) => {
|
|
@@ -2447,19 +3083,19 @@ class Path2D {
|
|
|
2447
3083
|
return this;
|
|
2448
3084
|
}
|
|
2449
3085
|
matrix(matrix) {
|
|
2450
|
-
this.
|
|
3086
|
+
this.curves.forEach((curve) => curve.matrix(matrix));
|
|
2451
3087
|
return this;
|
|
2452
3088
|
}
|
|
2453
3089
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN, withStyle = true) {
|
|
2454
3090
|
const strokeWidth = this.strokeWidth;
|
|
2455
|
-
this.
|
|
3091
|
+
this.curves.forEach((curve) => {
|
|
2456
3092
|
curve.getMinMax(min, max);
|
|
2457
3093
|
if (withStyle) {
|
|
2458
3094
|
if (strokeWidth > 1) {
|
|
2459
3095
|
const halfStrokeWidth = strokeWidth / 2;
|
|
2460
3096
|
const isClockwise = curve.isClockwise();
|
|
2461
3097
|
const points = [];
|
|
2462
|
-
for (let t = 0; t <= 1; t += 1 / curve.
|
|
3098
|
+
for (let t = 0; t <= 1; t += 1 / curve.arcLengthDivision) {
|
|
2463
3099
|
const point = curve.getPoint(t);
|
|
2464
3100
|
const normal = curve.getNormal(t);
|
|
2465
3101
|
const dist1 = normal.clone().scale(isClockwise ? halfStrokeWidth : -halfStrokeWidth);
|
|
@@ -2492,7 +3128,7 @@ class Path2D {
|
|
|
2492
3128
|
ctx.beginPath();
|
|
2493
3129
|
ctx.save();
|
|
2494
3130
|
setCanvasContext(ctx, style);
|
|
2495
|
-
this.
|
|
3131
|
+
this.curves.forEach((path) => {
|
|
2496
3132
|
path.drawTo(ctx);
|
|
2497
3133
|
});
|
|
2498
3134
|
if (fill !== "none") {
|
|
@@ -2510,9 +3146,8 @@ class Path2D {
|
|
|
2510
3146
|
ctx.beginPath();
|
|
2511
3147
|
ctx.save();
|
|
2512
3148
|
setCanvasContext(ctx, style);
|
|
2513
|
-
this.
|
|
2514
|
-
ctx
|
|
2515
|
-
ctx.arc(point.x, point.y, 4, 0, Math.PI * 2);
|
|
3149
|
+
this.getControlPointRefs().forEach((point) => {
|
|
3150
|
+
drawPoint(ctx, point.x, point.y, { radius: 4 });
|
|
2516
3151
|
});
|
|
2517
3152
|
if (fill !== "none") {
|
|
2518
3153
|
ctx.fill();
|
|
@@ -2524,10 +3159,10 @@ class Path2D {
|
|
|
2524
3159
|
return this;
|
|
2525
3160
|
}
|
|
2526
3161
|
toCommands() {
|
|
2527
|
-
return this.
|
|
3162
|
+
return this.curves.flatMap((v) => v.toCommands());
|
|
2528
3163
|
}
|
|
2529
3164
|
toData() {
|
|
2530
|
-
return this.
|
|
3165
|
+
return this.curves.filter((v) => v.curves.length).map((v) => v.toData()).join(" ");
|
|
2531
3166
|
}
|
|
2532
3167
|
toSVGPathString() {
|
|
2533
3168
|
const style = {
|
|
@@ -2552,10 +3187,34 @@ class Path2D {
|
|
|
2552
3187
|
}
|
|
2553
3188
|
return `<path d="${this.toData()}" style="${cssText}"></path>`;
|
|
2554
3189
|
}
|
|
3190
|
+
copy(source) {
|
|
3191
|
+
this.currentCurve = source.currentCurve.clone();
|
|
3192
|
+
this.curves = source.curves.map((path) => path.clone());
|
|
3193
|
+
this.style = { ...source.style };
|
|
3194
|
+
return this;
|
|
3195
|
+
}
|
|
3196
|
+
clone() {
|
|
3197
|
+
return new this.constructor().copy(this);
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
class Path2DSet {
|
|
3202
|
+
constructor(paths = []) {
|
|
3203
|
+
this.paths = paths;
|
|
3204
|
+
}
|
|
3205
|
+
getBoundingBox(withStyle = true) {
|
|
3206
|
+
if (!this.paths.length) {
|
|
3207
|
+
return void 0;
|
|
3208
|
+
}
|
|
3209
|
+
const min = Vector2.MAX;
|
|
3210
|
+
const max = Vector2.MIN;
|
|
3211
|
+
this.paths.forEach((path) => path.getMinMax(min, max, withStyle));
|
|
3212
|
+
return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
3213
|
+
}
|
|
2555
3214
|
toSVGString() {
|
|
2556
3215
|
const { x, y, width, height } = this.getBoundingBox();
|
|
2557
|
-
const
|
|
2558
|
-
return `<svg viewBox="${x} ${y} ${width} ${height}" width="${width}px" height="${height}px" xmlns="http://www.w3.org/2000/svg">${
|
|
3216
|
+
const content = this.paths.map((path) => path.toSVGPathString()).join("");
|
|
3217
|
+
return `<svg viewBox="${x} ${y} ${width} ${height}" width="${width}px" height="${height}px" xmlns="http://www.w3.org/2000/svg">${content}</svg>`;
|
|
2559
3218
|
}
|
|
2560
3219
|
toSVGUrl() {
|
|
2561
3220
|
return `data:image/svg+xml;base64,${btoa(this.toSVGString())}`;
|
|
@@ -2575,19 +3234,12 @@ class Path2D {
|
|
|
2575
3234
|
if (ctx) {
|
|
2576
3235
|
ctx.scale(pixelRatio, pixelRatio);
|
|
2577
3236
|
ctx.translate(-left, -top);
|
|
2578
|
-
this.
|
|
3237
|
+
this.paths.forEach((path) => {
|
|
3238
|
+
path.drawTo(ctx, style);
|
|
3239
|
+
});
|
|
2579
3240
|
}
|
|
2580
3241
|
return canvas;
|
|
2581
3242
|
}
|
|
2582
|
-
copy(source) {
|
|
2583
|
-
this.currentPath = source.currentPath.clone();
|
|
2584
|
-
this.paths = source.paths.map((path) => path.clone());
|
|
2585
|
-
this.style = { ...source.style };
|
|
2586
|
-
return this;
|
|
2587
|
-
}
|
|
2588
|
-
clone() {
|
|
2589
|
-
return new this.constructor().copy(this);
|
|
2590
|
-
}
|
|
2591
3243
|
}
|
|
2592
3244
|
|
|
2593
3245
|
const defaultUnit = "px";
|
|
@@ -3045,7 +3697,7 @@ function parseNode(node, style, paths = [], stylesheets = {}) {
|
|
|
3045
3697
|
break;
|
|
3046
3698
|
case "use": {
|
|
3047
3699
|
_style = parseStyle(node, _style, stylesheets);
|
|
3048
|
-
const href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href") || "";
|
|
3700
|
+
const href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href") || node.getAttribute("href") || "";
|
|
3049
3701
|
const usedNodeId = href.substring(1);
|
|
3050
3702
|
const usedNode = node.viewportElement?.getElementById(usedNodeId);
|
|
3051
3703
|
if (usedNode) {
|
|
@@ -3116,73 +3768,38 @@ ${xml}`);
|
|
|
3116
3768
|
}
|
|
3117
3769
|
}
|
|
3118
3770
|
function parseSVG(svg) {
|
|
3119
|
-
return parseNode(parseSVGToDOM(svg), {});
|
|
3120
|
-
}
|
|
3121
|
-
|
|
3122
|
-
function getPathsBoundingBox(paths, withStyle = true) {
|
|
3123
|
-
if (!paths.length) {
|
|
3124
|
-
return void 0;
|
|
3125
|
-
}
|
|
3126
|
-
const min = Vector2.MAX;
|
|
3127
|
-
const max = Vector2.MIN;
|
|
3128
|
-
paths.forEach((path) => path.getMinMax(min, max, withStyle));
|
|
3129
|
-
return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
3130
|
-
}
|
|
3131
|
-
function pathsToSVGString(paths) {
|
|
3132
|
-
const { x, y, width, height } = getPathsBoundingBox(paths);
|
|
3133
|
-
const content = paths.map((path) => path.toSVGPathString()).join("");
|
|
3134
|
-
return `<svg viewBox="${x} ${y} ${width} ${height}" width="${width}px" height="${height}px" xmlns="http://www.w3.org/2000/svg">${content}</svg>`;
|
|
3135
|
-
}
|
|
3136
|
-
function pathsToSVGUrl(paths) {
|
|
3137
|
-
return `data:image/svg+xml;base64,${btoa(pathsToSVGString(paths))}`;
|
|
3138
|
-
}
|
|
3139
|
-
function pathsToSVG(paths) {
|
|
3140
|
-
return new DOMParser().parseFromString(pathsToSVGString(paths), "image/svg+xml").documentElement;
|
|
3141
|
-
}
|
|
3142
|
-
function pathsToCanvas(paths, options = {}) {
|
|
3143
|
-
const { pixelRatio = 2, ...style } = options;
|
|
3144
|
-
const { left, top, width, height } = getPathsBoundingBox(paths);
|
|
3145
|
-
const canvas = document.createElement("canvas");
|
|
3146
|
-
canvas.width = width * pixelRatio;
|
|
3147
|
-
canvas.height = height * pixelRatio;
|
|
3148
|
-
canvas.style.width = `${width}px`;
|
|
3149
|
-
canvas.style.height = `${height}px`;
|
|
3150
|
-
const ctx = canvas.getContext("2d");
|
|
3151
|
-
if (ctx) {
|
|
3152
|
-
ctx.scale(pixelRatio, pixelRatio);
|
|
3153
|
-
ctx.translate(-left, -top);
|
|
3154
|
-
paths.forEach((path) => {
|
|
3155
|
-
path.drawTo(ctx, style);
|
|
3156
|
-
});
|
|
3157
|
-
}
|
|
3158
|
-
return canvas;
|
|
3771
|
+
return new Path2DSet(parseNode(parseSVGToDOM(svg), {}));
|
|
3159
3772
|
}
|
|
3160
3773
|
|
|
3774
|
+
exports.ArcCurve = ArcCurve;
|
|
3161
3775
|
exports.BoundingBox = BoundingBox;
|
|
3162
|
-
exports.
|
|
3776
|
+
exports.CompositeCurve = CompositeCurve;
|
|
3163
3777
|
exports.CubicBezierCurve = CubicBezierCurve;
|
|
3164
3778
|
exports.Curve = Curve;
|
|
3165
3779
|
exports.CurvePath = CurvePath;
|
|
3166
3780
|
exports.EllipseCurve = EllipseCurve;
|
|
3167
|
-
exports.HeartCurve = HeartCurve;
|
|
3168
3781
|
exports.LineCurve = LineCurve;
|
|
3169
3782
|
exports.Matrix3 = Matrix3;
|
|
3170
3783
|
exports.Path2D = Path2D;
|
|
3784
|
+
exports.Path2DSet = Path2DSet;
|
|
3171
3785
|
exports.PloygonCurve = PloygonCurve;
|
|
3172
3786
|
exports.QuadraticBezierCurve = QuadraticBezierCurve;
|
|
3173
|
-
exports.
|
|
3787
|
+
exports.RectangleCurve = RectangleCurve;
|
|
3174
3788
|
exports.SplineCurve = SplineCurve;
|
|
3175
3789
|
exports.Vector2 = Vector2;
|
|
3176
|
-
exports.
|
|
3177
|
-
exports.
|
|
3790
|
+
exports.catmullRom = catmullRom;
|
|
3791
|
+
exports.cubicBezier = cubicBezier;
|
|
3792
|
+
exports.drawPoint = drawPoint;
|
|
3793
|
+
exports.fillTriangulate = fillTriangulate;
|
|
3794
|
+
exports.getAdaptiveCubicBezierCurvePoints = getAdaptiveCubicBezierCurvePoints;
|
|
3795
|
+
exports.getAdaptiveQuadraticBezierCurvePoints = getAdaptiveQuadraticBezierCurvePoints;
|
|
3178
3796
|
exports.parseArcCommand = parseArcCommand;
|
|
3179
3797
|
exports.parsePathDataArgs = parsePathDataArgs;
|
|
3180
3798
|
exports.parseSVG = parseSVG;
|
|
3181
3799
|
exports.parseSVGToDOM = parseSVGToDOM;
|
|
3182
|
-
exports.
|
|
3183
|
-
exports.pathDataToPathCommands = pathDataToPathCommands;
|
|
3184
|
-
exports.pathsToCanvas = pathsToCanvas;
|
|
3185
|
-
exports.pathsToSVG = pathsToSVG;
|
|
3186
|
-
exports.pathsToSVGString = pathsToSVGString;
|
|
3187
|
-
exports.pathsToSVGUrl = pathsToSVGUrl;
|
|
3800
|
+
exports.quadraticBezier = quadraticBezier;
|
|
3188
3801
|
exports.setCanvasContext = setCanvasContext;
|
|
3802
|
+
exports.strokeTriangulate = strokeTriangulate;
|
|
3803
|
+
exports.svgPathCommandsAddToPath2D = svgPathCommandsAddToPath2D;
|
|
3804
|
+
exports.svgPathCommandsToData = svgPathCommandsToData;
|
|
3805
|
+
exports.svgPathDataToCommands = svgPathDataToCommands;
|