svg-path-commander 0.1.21 → 0.1.22
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 +72 -12
- package/dist/svg-path-commander.esm.js +647 -190
- package/dist/svg-path-commander.esm.min.js +2 -2
- package/dist/svg-path-commander.js +670 -189
- package/dist/svg-path-commander.min.js +2 -2
- package/package.json +1 -1
- package/src/convert/pathToAbsolute.js +3 -2
- package/src/convert/pathToCurve.js +2 -1
- package/src/convert/pathToRelative.js +2 -1
- package/src/parser/paramsCount.js +1 -0
- package/src/parser/parsePathString.js +4 -3
- package/src/parser/pathParser.js +2 -1
- package/src/parser/scanSegment.js +0 -1
- package/src/process/fixPath.js +1 -1
- package/src/process/lineToCubic.js +4 -5
- package/src/process/normalizePath.js +4 -2
- package/src/util/getClosestPoint.js +12 -0
- package/src/util/getCubicSize.js +41 -39
- package/src/util/getPathArea.js +19 -19
- package/src/util/getPathBBox.js +0 -11
- package/src/util/getPathLength.js +9 -9
- package/src/util/getPointAtLength.js +6 -29
- package/src/util/getPointAtPathLength.js +44 -0
- package/src/util/getPropertiesAtLength.js +62 -0
- package/src/util/getPropertiesAtPoint.js +77 -0
- package/src/util/getSegmentAtLength.js +13 -0
- package/src/util/getSegmentOfPoint.js +14 -0
- package/src/util/getTotalLength.js +15 -0
- package/src/util/isAbsoluteArray.js +2 -1
- package/src/util/isCurveArray.js +2 -1
- package/src/util/isNormalizedArray.js +2 -1
- package/src/util/isPointInStroke.js +13 -0
- package/src/util/isRelativeArray.js +2 -1
- package/src/util/pathLengthFactory.js +99 -0
- package/src/util/segmentArcFactory.js +42 -0
- package/src/util/segmentCubicFactory.js +73 -0
- package/src/util/segmentLineFactory.js +30 -0
- package/src/util/segmentQuadFactory.js +70 -0
- package/src/util/util.js +19 -1
- package/types/index.d.ts +14 -5
- package/types/more/modules.ts +17 -5
- package/types/more/svg.d.ts +21 -0
- package/types/svg-path-commander.d.ts +242 -122
- package/src/util/getPointAtSegLength.js +0 -28
- package/src/util/getSegArcLength.js +0 -27
- package/src/util/getSegCubicLength.js +0 -52
- package/src/util/getSegLineLength.js +0 -14
- package/src/util/getSegQuadLength.js +0 -31
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* SVGPathCommander v0.1.
|
|
2
|
+
* SVGPathCommander v0.1.22 (http://thednp.github.io/svg-path-commander)
|
|
3
3
|
* Copyright 2021 © thednp
|
|
4
4
|
* Licensed under MIT (https://github.com/thednp/svg-path-commander/blob/master/LICENSE)
|
|
5
5
|
*/
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Segment params length
|
|
23
|
+
* @type {Record<string, number>}
|
|
23
24
|
*/
|
|
24
25
|
var paramsCount = {
|
|
25
26
|
a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0,
|
|
@@ -283,7 +284,6 @@
|
|
|
283
284
|
var pathValue = path.pathValue;
|
|
284
285
|
var index = path.index;
|
|
285
286
|
var cmdCode = pathValue.charCodeAt(index);
|
|
286
|
-
// @ts-ignore
|
|
287
287
|
var reqParams = paramsCount[pathValue[index].toLowerCase()];
|
|
288
288
|
|
|
289
289
|
path.segmentStart = index;
|
|
@@ -347,7 +347,8 @@
|
|
|
347
347
|
}
|
|
348
348
|
|
|
349
349
|
/**
|
|
350
|
-
* The `PathParser` used by the
|
|
350
|
+
* The `PathParser` is used by the `parsePathString` static method
|
|
351
|
+
* to generate a `pathArray`.
|
|
351
352
|
*
|
|
352
353
|
* @param {string} pathString
|
|
353
354
|
*/
|
|
@@ -392,12 +393,13 @@
|
|
|
392
393
|
* @returns {SVGPathCommander.pathArray} the resulted `pathArray`
|
|
393
394
|
*/
|
|
394
395
|
function parsePathString(pathInput) {
|
|
395
|
-
if (
|
|
396
|
+
if (isPathArray(pathInput)) {
|
|
397
|
+
// @ts-ignore -- isPathArray also checks if it's an `Array`
|
|
396
398
|
return clonePath(pathInput);
|
|
397
399
|
}
|
|
398
400
|
|
|
399
|
-
// @ts-ignore
|
|
400
|
-
var path = new PathParser(pathInput);
|
|
401
|
+
// @ts-ignore -- pathInput is now string
|
|
402
|
+
var path = new PathParser(pathInput);
|
|
401
403
|
|
|
402
404
|
skipSpaces(path);
|
|
403
405
|
|
|
@@ -425,11 +427,12 @@
|
|
|
425
427
|
* Iterates an array to check if it's a `pathArray`
|
|
426
428
|
* with all absolute values.
|
|
427
429
|
*
|
|
428
|
-
* @param {SVGPathCommander.pathArray} path the `pathArray` to be checked
|
|
430
|
+
* @param {string | SVGPathCommander.pathArray} path the `pathArray` to be checked
|
|
429
431
|
* @returns {boolean} iteration result
|
|
430
432
|
*/
|
|
431
433
|
function isAbsoluteArray(path) {
|
|
432
434
|
return isPathArray(path)
|
|
435
|
+
// @ts-ignore -- `isPathArray` also checks if it's `Array`
|
|
433
436
|
&& path.every(function (x) { return x[0] === x[0].toUpperCase(); });
|
|
434
437
|
}
|
|
435
438
|
|
|
@@ -437,11 +440,12 @@
|
|
|
437
440
|
* Parses a path string value or object and returns an array
|
|
438
441
|
* of segments, all converted to absolute values.
|
|
439
442
|
*
|
|
440
|
-
* @param {SVGPathCommander.pathArray
|
|
443
|
+
* @param {string | SVGPathCommander.pathArray} pathInput the path string | object
|
|
441
444
|
* @returns {SVGPathCommander.absoluteArray} the resulted `pathArray` with absolute values
|
|
442
445
|
*/
|
|
443
446
|
function pathToAbsolute(pathInput) {
|
|
444
|
-
if (
|
|
447
|
+
if (isAbsoluteArray(pathInput)) {
|
|
448
|
+
// @ts-ignore -- `isAbsoluteArray` checks if it's `pathArray`
|
|
445
449
|
return clonePath(pathInput);
|
|
446
450
|
}
|
|
447
451
|
|
|
@@ -528,11 +532,12 @@
|
|
|
528
532
|
* Iterates an array to check if it's a `pathArray`
|
|
529
533
|
* with relative values.
|
|
530
534
|
*
|
|
531
|
-
* @param {SVGPathCommander.pathArray} path the `pathArray` to be checked
|
|
535
|
+
* @param {string | SVGPathCommander.pathArray} path the `pathArray` to be checked
|
|
532
536
|
* @returns {boolean} iteration result
|
|
533
537
|
*/
|
|
534
538
|
function isRelativeArray(path) {
|
|
535
539
|
return isPathArray(path)
|
|
540
|
+
// @ts-ignore -- `isPathArray` checks if it's `Array`
|
|
536
541
|
&& path.slice(1).every(function (seg) { return seg[0] === seg[0].toLowerCase(); });
|
|
537
542
|
}
|
|
538
543
|
|
|
@@ -540,11 +545,12 @@
|
|
|
540
545
|
* Parses a path string value or object and returns an array
|
|
541
546
|
* of segments, all converted to relative values.
|
|
542
547
|
*
|
|
543
|
-
* @param {SVGPathCommander.pathArray} pathInput the path string | object
|
|
548
|
+
* @param {string | SVGPathCommander.pathArray} pathInput the path string | object
|
|
544
549
|
* @returns {SVGPathCommander.relativeArray} the resulted `pathArray` with relative values
|
|
545
550
|
*/
|
|
546
551
|
function pathToRelative(pathInput) {
|
|
547
552
|
if (isRelativeArray(pathInput)) {
|
|
553
|
+
// @ts-ignore -- `isRelativeArray` checks if it's `pathArray`
|
|
548
554
|
return clonePath(pathInput);
|
|
549
555
|
}
|
|
550
556
|
|
|
@@ -749,10 +755,11 @@
|
|
|
749
755
|
* with all segments are in non-shorthand notation
|
|
750
756
|
* with absolute values.
|
|
751
757
|
*
|
|
752
|
-
* @param {SVGPathCommander.pathArray} path the `pathArray` to be checked
|
|
758
|
+
* @param {string | SVGPathCommander.pathArray} path the `pathArray` to be checked
|
|
753
759
|
* @returns {boolean} iteration result
|
|
754
760
|
*/
|
|
755
761
|
function isNormalizedArray(path) {
|
|
762
|
+
// @ts-ignore -- `isAbsoluteArray` also checks if it's `Array`
|
|
756
763
|
return isAbsoluteArray(path) && path.every(function (seg) { return 'ACLMQZ'.includes(seg[0]); });
|
|
757
764
|
}
|
|
758
765
|
|
|
@@ -768,16 +775,19 @@
|
|
|
768
775
|
* * convert segments to absolute values
|
|
769
776
|
* * convert shorthand path commands to their non-shorthand notation
|
|
770
777
|
*
|
|
771
|
-
* @param {SVGPathCommander.pathArray} pathInput the string to be parsed or 'pathArray'
|
|
778
|
+
* @param {string | SVGPathCommander.pathArray} pathInput the string to be parsed or 'pathArray'
|
|
772
779
|
* @returns {SVGPathCommander.normalArray} the normalized `pathArray`
|
|
773
780
|
*/
|
|
774
781
|
function normalizePath(pathInput) {
|
|
775
782
|
var assign;
|
|
776
783
|
|
|
777
784
|
if (isNormalizedArray(pathInput)) {
|
|
785
|
+
// @ts-ignore -- `isNormalizedArray` checks if it's `pathArray`
|
|
778
786
|
return clonePath(pathInput);
|
|
779
787
|
}
|
|
780
788
|
|
|
789
|
+
/** @type {SVGPathCommander.normalArray} */
|
|
790
|
+
// @ts-ignore -- `absoluteArray` will become a `normalArray`
|
|
781
791
|
var path = pathToAbsolute(pathInput);
|
|
782
792
|
var params = Object.assign({}, paramsParser);
|
|
783
793
|
var allPathCommands = [];
|
|
@@ -805,7 +815,6 @@
|
|
|
805
815
|
params.y2 = +(segment[seglen - 3]) || params.y1;
|
|
806
816
|
}
|
|
807
817
|
|
|
808
|
-
// @ts-ignore -- a `normalArray` is absolutely an `absoluteArray`
|
|
809
818
|
return path;
|
|
810
819
|
}
|
|
811
820
|
|
|
@@ -836,7 +845,7 @@
|
|
|
836
845
|
var y = ref$1[1];
|
|
837
846
|
|
|
838
847
|
if (isClosed && mx === x && my === y) {
|
|
839
|
-
// @ts-ignore -- `pathSegment[]` is a `pathArray`
|
|
848
|
+
// @ts-ignore -- `pathSegment[]` is quite a `pathArray`
|
|
840
849
|
return pathArray.slice(0, -1);
|
|
841
850
|
}
|
|
842
851
|
return pathArray;
|
|
@@ -846,10 +855,11 @@
|
|
|
846
855
|
* Iterates an array to check if it's a `pathArray`
|
|
847
856
|
* with all C (cubic bezier) segments.
|
|
848
857
|
*
|
|
849
|
-
* @param {SVGPathCommander.pathArray} path the `Array` to be checked
|
|
858
|
+
* @param {string | SVGPathCommander.pathArray} path the `Array` to be checked
|
|
850
859
|
* @returns {boolean} iteration result
|
|
851
860
|
*/
|
|
852
861
|
function isCurveArray(path) {
|
|
862
|
+
// @ts-ignore -- `isPathArray` also checks if it's `Array`
|
|
853
863
|
return isPathArray(path) && path.every(function (seg) { return 'MC'.includes(seg[0]); });
|
|
854
864
|
}
|
|
855
865
|
|
|
@@ -1005,35 +1015,6 @@
|
|
|
1005
1015
|
x2, y2 ];
|
|
1006
1016
|
}
|
|
1007
1017
|
|
|
1008
|
-
/**
|
|
1009
|
-
* Returns the {x,y} coordinates of a point at a
|
|
1010
|
-
* given length of a cubic-bezier segment.
|
|
1011
|
-
*
|
|
1012
|
-
* @param {number} p1x the starting point X
|
|
1013
|
-
* @param {number} p1y the starting point Y
|
|
1014
|
-
* @param {number} c1x the first control point X
|
|
1015
|
-
* @param {number} c1y the first control point Y
|
|
1016
|
-
* @param {number} c2x the second control point X
|
|
1017
|
-
* @param {number} c2y the second control point Y
|
|
1018
|
-
* @param {number} p2x the ending point X
|
|
1019
|
-
* @param {number} p2y the ending point Y
|
|
1020
|
-
* @param {number} t a [0-1] ratio
|
|
1021
|
-
* @returns {{x: number, y: number}} the requested {x,y} coordinates
|
|
1022
|
-
*/
|
|
1023
|
-
function getPointAtSegLength(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
|
|
1024
|
-
var t1 = 1 - t;
|
|
1025
|
-
return {
|
|
1026
|
-
x: (Math.pow( t1, 3 )) * p1x
|
|
1027
|
-
+ t1 * t1 * 3 * t * c1x
|
|
1028
|
-
+ t1 * 3 * t * t * c2x
|
|
1029
|
-
+ (Math.pow( t, 3 )) * p2x,
|
|
1030
|
-
y: (Math.pow( t1, 3 )) * p1y
|
|
1031
|
-
+ t1 * t1 * 3 * t * c1y
|
|
1032
|
-
+ t1 * 3 * t * t * c2y
|
|
1033
|
-
+ (Math.pow( t, 3 )) * p2y,
|
|
1034
|
-
};
|
|
1035
|
-
}
|
|
1036
|
-
|
|
1037
1018
|
/**
|
|
1038
1019
|
* Returns the coordinates of a specified distance
|
|
1039
1020
|
* ratio between two points.
|
|
@@ -1050,6 +1031,51 @@
|
|
|
1050
1031
|
return [ax + (bx - ax) * t, ay + (by - ay) * t];
|
|
1051
1032
|
}
|
|
1052
1033
|
|
|
1034
|
+
/**
|
|
1035
|
+
* Returns the square root of the distance
|
|
1036
|
+
* between two given points.
|
|
1037
|
+
*
|
|
1038
|
+
* @param {[number, number]} a the first point coordinates
|
|
1039
|
+
* @param {[number, number]} b the second point coordinates
|
|
1040
|
+
* @returns {number} the distance value
|
|
1041
|
+
*/
|
|
1042
|
+
function distanceSquareRoot(a, b) {
|
|
1043
|
+
return Math.sqrt(
|
|
1044
|
+
(a[0] - b[0]) * (a[0] - b[0])
|
|
1045
|
+
+ (a[1] - b[1]) * (a[1] - b[1])
|
|
1046
|
+
);
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
/**
|
|
1050
|
+
* Returns the length of a line (L,V,H,Z) segment,
|
|
1051
|
+
* or a point at a given length.
|
|
1052
|
+
*
|
|
1053
|
+
* @param {number} x1 the starting point X
|
|
1054
|
+
* @param {number} y1 the starting point Y
|
|
1055
|
+
* @param {number} x2 the ending point X
|
|
1056
|
+
* @param {number} y2 the ending point Y
|
|
1057
|
+
* @param {number=} distance the distance to point
|
|
1058
|
+
* @returns {{x: number, y: number} | number} the segment length or point
|
|
1059
|
+
*/
|
|
1060
|
+
function segmentLineFactory(x1, y1, x2, y2, distance) {
|
|
1061
|
+
var length = distanceSquareRoot([x1, y1], [x2, y2]);
|
|
1062
|
+
var margin = 0.001;
|
|
1063
|
+
|
|
1064
|
+
if (typeof distance === 'number') {
|
|
1065
|
+
if (distance < margin) {
|
|
1066
|
+
return { x: x1, y: y1 };
|
|
1067
|
+
}
|
|
1068
|
+
if (distance > length + margin) {
|
|
1069
|
+
return { x: x2, y: y2 };
|
|
1070
|
+
}
|
|
1071
|
+
var ref = midPoint([x1, y1], [x2, y2], distance / length);
|
|
1072
|
+
var x = ref[0];
|
|
1073
|
+
var y = ref[1];
|
|
1074
|
+
return { x: x, y: y };
|
|
1075
|
+
}
|
|
1076
|
+
return length;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1053
1079
|
/**
|
|
1054
1080
|
* Converts an L (line-to) segment to C (cubic-bezier).
|
|
1055
1081
|
*
|
|
@@ -1070,15 +1096,14 @@
|
|
|
1070
1096
|
var p4 = midPoint(p2, p3, t);
|
|
1071
1097
|
var p5 = midPoint(p3, p4, t);
|
|
1072
1098
|
var p6 = midPoint(p4, p5, t);
|
|
1073
|
-
// const cp1 = getPointAtSegLength.apply(0, p0.concat(p2, p4, p6, t));
|
|
1074
1099
|
var seg1 = p0.concat( p2, p4, p6, [t]);
|
|
1075
1100
|
// @ts-ignore
|
|
1076
|
-
var cp1 =
|
|
1077
|
-
// const cp2 = getPointAtSegLength.apply(0, p6.concat(p5, p3, p1, 0));
|
|
1101
|
+
var cp1 = segmentLineFactory.apply(void 0, seg1);
|
|
1078
1102
|
var seg2 = p6.concat( p5, p3, p1, [0]);
|
|
1079
1103
|
// @ts-ignore
|
|
1080
|
-
var cp2 =
|
|
1104
|
+
var cp2 = segmentLineFactory.apply(void 0, seg2);
|
|
1081
1105
|
|
|
1106
|
+
// @ts-ignore
|
|
1082
1107
|
return [cp1.x, cp1.y, cp2.x, cp2.y, x2, y2];
|
|
1083
1108
|
}
|
|
1084
1109
|
|
|
@@ -1138,13 +1163,14 @@
|
|
|
1138
1163
|
* In addition, un-necessary `Z` segment is removed if previous segment
|
|
1139
1164
|
* extends to the `M` segment.
|
|
1140
1165
|
*
|
|
1141
|
-
* @param {SVGPathCommander.pathArray} pathInput the string to be parsed or 'pathArray'
|
|
1166
|
+
* @param {string | SVGPathCommander.pathArray} pathInput the string to be parsed or 'pathArray'
|
|
1142
1167
|
* @returns {SVGPathCommander.curveArray} the resulted `pathArray` converted to cubic-bezier
|
|
1143
1168
|
*/
|
|
1144
1169
|
function pathToCurve(pathInput) {
|
|
1145
1170
|
var assign;
|
|
1146
1171
|
|
|
1147
1172
|
if (isCurveArray(pathInput)) {
|
|
1173
|
+
// @ts-ignore -- `isCurveArray` checks if it's `pathArray`
|
|
1148
1174
|
return clonePath(pathInput);
|
|
1149
1175
|
}
|
|
1150
1176
|
|
|
@@ -2622,65 +2648,143 @@
|
|
|
2622
2648
|
}
|
|
2623
2649
|
|
|
2624
2650
|
/**
|
|
2625
|
-
* Returns
|
|
2651
|
+
* Returns a point at a given length of a C (cubic-bezier) segment.
|
|
2652
|
+
*
|
|
2653
|
+
* @param {number} x1 the starting point X
|
|
2654
|
+
* @param {number} y1 the starting point Y
|
|
2655
|
+
* @param {number} c1x the first control point X
|
|
2656
|
+
* @param {number} c1y the first control point Y
|
|
2657
|
+
* @param {number} c2x the second control point X
|
|
2658
|
+
* @param {number} c2y the second control point Y
|
|
2659
|
+
* @param {number} x2 the ending point X
|
|
2660
|
+
* @param {number} y2 the ending point Y
|
|
2661
|
+
* @param {number} t a [0-1] ratio
|
|
2662
|
+
* @returns {{x: number, y: number}} the cubic-bezier segment length
|
|
2663
|
+
*/
|
|
2664
|
+
function getPointAtCubicSegmentLength(x1, y1, c1x, c1y, c2x, c2y, x2, y2, t) {
|
|
2665
|
+
var t1 = 1 - t;
|
|
2666
|
+
return {
|
|
2667
|
+
x: (Math.pow( t1, 3 )) * x1
|
|
2668
|
+
+ 3 * (Math.pow( t1, 2 )) * t * c1x
|
|
2669
|
+
+ 3 * t1 * (Math.pow( t, 2 )) * c2x
|
|
2670
|
+
+ (Math.pow( t, 3 )) * x2,
|
|
2671
|
+
y: (Math.pow( t1, 3 )) * y1
|
|
2672
|
+
+ 3 * (Math.pow( t1, 2 )) * t * c1y
|
|
2673
|
+
+ 3 * t1 * (Math.pow( t, 2 )) * c2y
|
|
2674
|
+
+ (Math.pow( t, 3 )) * y2,
|
|
2675
|
+
};
|
|
2676
|
+
}
|
|
2677
|
+
|
|
2678
|
+
/**
|
|
2679
|
+
* Returns the length of a C (cubic-bezier) segment,
|
|
2680
|
+
* or an {x,y} point at a given length.
|
|
2681
|
+
*
|
|
2682
|
+
* @param {number} x1 the starting point X
|
|
2683
|
+
* @param {number} y1 the starting point Y
|
|
2684
|
+
* @param {number} c1x the first control point X
|
|
2685
|
+
* @param {number} c1y the first control point Y
|
|
2686
|
+
* @param {number} c2x the second control point X
|
|
2687
|
+
* @param {number} c2y the second control point Y
|
|
2688
|
+
* @param {number} x2 the ending point X
|
|
2689
|
+
* @param {number} y2 the ending point Y
|
|
2690
|
+
* @param {number=} distance the point distance
|
|
2691
|
+
* @returns {{x: number, y: number} | number} the segment length or point
|
|
2692
|
+
*/
|
|
2693
|
+
function segmentCubicFactory(x1, y1, c1x, c1y, c2x, c2y, x2, y2, distance) {
|
|
2694
|
+
var assign;
|
|
2695
|
+
|
|
2696
|
+
var x = x1; var y = y1;
|
|
2697
|
+
var totalLength = 0;
|
|
2698
|
+
var prev = [x1, y1, totalLength];
|
|
2699
|
+
/** @type {[number, number]} */
|
|
2700
|
+
var cur = [x1, y1];
|
|
2701
|
+
var t = 0;
|
|
2702
|
+
|
|
2703
|
+
var n = 101;
|
|
2704
|
+
for (var j = 0; j <= n; j += 1) {
|
|
2705
|
+
t = j / n;
|
|
2706
|
+
|
|
2707
|
+
((assign = getPointAtCubicSegmentLength(x1, y1, c1x, c1y, c2x, c2y, x2, y2, t), x = assign.x, y = assign.y));
|
|
2708
|
+
totalLength += distanceSquareRoot(cur, [x, y]);
|
|
2709
|
+
cur = [x, y];
|
|
2710
|
+
|
|
2711
|
+
if (typeof distance === 'number' && totalLength >= distance) {
|
|
2712
|
+
var dv = (totalLength - distance) / (totalLength - prev[2]);
|
|
2713
|
+
|
|
2714
|
+
return {
|
|
2715
|
+
x: cur[0] * (1 - dv) + prev[0] * dv,
|
|
2716
|
+
y: cur[1] * (1 - dv) + prev[1] * dv,
|
|
2717
|
+
};
|
|
2718
|
+
}
|
|
2719
|
+
prev = [x, y, totalLength];
|
|
2720
|
+
}
|
|
2721
|
+
return totalLength;
|
|
2722
|
+
}
|
|
2723
|
+
|
|
2724
|
+
/**
|
|
2725
|
+
* Returns the cubic-bezier segment bounding box.
|
|
2626
2726
|
*
|
|
2627
|
-
* @param {number}
|
|
2628
|
-
* @param {number}
|
|
2727
|
+
* @param {number} x1 the starting point X
|
|
2728
|
+
* @param {number} y1 the starting point Y
|
|
2629
2729
|
* @param {number} c1x the first control point X
|
|
2630
2730
|
* @param {number} c1y the first control point Y
|
|
2631
2731
|
* @param {number} c2x the second control point X
|
|
2632
2732
|
* @param {number} c2y the second control point Y
|
|
2633
|
-
* @param {number}
|
|
2634
|
-
* @param {number}
|
|
2635
|
-
* @returns {SVGPathCommander.segmentLimits} the
|
|
2636
|
-
*/
|
|
2637
|
-
function getCubicSize(
|
|
2638
|
-
var
|
|
2639
|
-
|
|
2640
|
-
var
|
|
2733
|
+
* @param {number} x2 the ending point X
|
|
2734
|
+
* @param {number} y2 the ending point Y
|
|
2735
|
+
* @returns {SVGPathCommander.segmentLimits} the bounding box of the cubic-bezier segment
|
|
2736
|
+
*/
|
|
2737
|
+
function getCubicSize(x1, y1, c1x, c1y, c2x, c2y, x2, y2) {
|
|
2738
|
+
var assign, assign$1, assign$2, assign$3;
|
|
2739
|
+
|
|
2740
|
+
var a = (c2x - 2 * c1x + x1) - (x2 - 2 * c2x + c1x);
|
|
2741
|
+
var b = 2 * (c1x - x1) - 2 * (c2x - c1x);
|
|
2742
|
+
var c = x1 - c1x;
|
|
2641
2743
|
var t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a;
|
|
2642
2744
|
var t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a;
|
|
2643
|
-
var
|
|
2644
|
-
var
|
|
2645
|
-
var
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
if (Math.abs(t2) >
|
|
2745
|
+
var X = [x1, x2];
|
|
2746
|
+
var Y = [y1, y2];
|
|
2747
|
+
var x = 0;
|
|
2748
|
+
var y = 0;
|
|
2749
|
+
|
|
2750
|
+
if (Math.abs(t1) > 1e12) { t1 = 0.5; }
|
|
2751
|
+
if (Math.abs(t2) > 1e12) { t2 = 0.5; }
|
|
2650
2752
|
|
|
2651
2753
|
if (t1 > 0 && t1 < 1) {
|
|
2652
|
-
|
|
2653
|
-
x.
|
|
2654
|
-
|
|
2754
|
+
// @ts-ignore
|
|
2755
|
+
((assign = segmentCubicFactory(x1, y1, c1x, c1y, c2x, c2y, x2, y2, t1), x = assign.x, y = assign.y));
|
|
2756
|
+
X.push(x);
|
|
2757
|
+
Y.push(y);
|
|
2655
2758
|
}
|
|
2656
2759
|
if (t2 > 0 && t2 < 1) {
|
|
2657
|
-
|
|
2658
|
-
x.
|
|
2659
|
-
|
|
2760
|
+
// @ts-ignore
|
|
2761
|
+
((assign$1 = segmentCubicFactory(x1, y1, c1x, c1y, c2x, c2y, x2, y2, t2), x = assign$1.x, y = assign$1.y));
|
|
2762
|
+
X.push(x);
|
|
2763
|
+
Y.push(y);
|
|
2660
2764
|
}
|
|
2661
|
-
a = (c2y - 2 * c1y +
|
|
2662
|
-
b = 2 * (c1y -
|
|
2663
|
-
c =
|
|
2765
|
+
a = (c2y - 2 * c1y + y1) - (y2 - 2 * c2y + c1y);
|
|
2766
|
+
b = 2 * (c1y - y1) - 2 * (c2y - c1y);
|
|
2767
|
+
c = y1 - c1y;
|
|
2664
2768
|
t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a;
|
|
2665
2769
|
t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a;
|
|
2666
|
-
|
|
2667
|
-
if (Math.abs(
|
|
2668
|
-
// @ts-ignore
|
|
2669
|
-
if (Math.abs(t2) > '1e12') { t2 = 0.5; }
|
|
2770
|
+
if (Math.abs(t1) > 1e12) { t1 = 0.5; }
|
|
2771
|
+
if (Math.abs(t2) > 1e12) { t2 = 0.5; }
|
|
2670
2772
|
|
|
2671
2773
|
if (t1 > 0 && t1 < 1) {
|
|
2672
|
-
|
|
2673
|
-
x.
|
|
2674
|
-
|
|
2774
|
+
// @ts-ignore
|
|
2775
|
+
((assign$2 = segmentCubicFactory(x1, y1, c1x, c1y, c2x, c2y, x2, y2, t1), x = assign$2.x, y = assign$2.y));
|
|
2776
|
+
X.push(x);
|
|
2777
|
+
Y.push(y);
|
|
2675
2778
|
}
|
|
2676
2779
|
if (t2 > 0 && t2 < 1) {
|
|
2677
|
-
|
|
2678
|
-
x.
|
|
2679
|
-
|
|
2780
|
+
// @ts-ignore
|
|
2781
|
+
((assign$3 = segmentCubicFactory(x1, y1, c1x, c1y, c2x, c2y, x2, y2, t2), x = assign$3.x, y = assign$3.y));
|
|
2782
|
+
X.push(x);
|
|
2783
|
+
Y.push(y);
|
|
2680
2784
|
}
|
|
2681
2785
|
return {
|
|
2682
|
-
min: { x: Math.min.apply(
|
|
2683
|
-
max: { x: Math.max.apply(
|
|
2786
|
+
min: { x: Math.min.apply(Math, X), y: Math.min.apply(Math, Y) },
|
|
2787
|
+
max: { x: Math.max.apply(Math, X), y: Math.max.apply(Math, Y) },
|
|
2684
2788
|
};
|
|
2685
2789
|
}
|
|
2686
2790
|
|
|
@@ -2718,27 +2822,16 @@
|
|
|
2718
2822
|
// @ts-ignore -- this should be fine
|
|
2719
2823
|
var dim = getCubicSize.apply(void 0, sizeArgs);
|
|
2720
2824
|
|
|
2721
|
-
// X = X.concat(dim.min.x, dim.max.x);
|
|
2722
2825
|
X = X.concat( [dim.min.x], [dim.max.x]);
|
|
2723
|
-
|
|
2724
|
-
// Y = Y.concat(dim.min.y, dim.max.y);
|
|
2725
2826
|
Y = Y.concat( [dim.min.y], [dim.max.y]);
|
|
2726
2827
|
x = s1;
|
|
2727
2828
|
y = s2;
|
|
2728
2829
|
}
|
|
2729
2830
|
});
|
|
2730
2831
|
|
|
2731
|
-
// @ts-ignore
|
|
2732
|
-
// const xTop = Math.min.apply(0, X);
|
|
2733
2832
|
var xTop = Math.min.apply(Math, X);
|
|
2734
|
-
// @ts-ignore
|
|
2735
|
-
// const yTop = Math.min.apply(0, Y);
|
|
2736
2833
|
var yTop = Math.min.apply(Math, Y);
|
|
2737
|
-
// @ts-ignore
|
|
2738
|
-
// const xBot = Math.max.apply(0, X);
|
|
2739
2834
|
var xBot = Math.max.apply(Math, X);
|
|
2740
|
-
// @ts-ignore
|
|
2741
|
-
// const yBot = Math.max.apply(0, Y);
|
|
2742
2835
|
var yBot = Math.max.apply(Math, Y);
|
|
2743
2836
|
var width = xBot - xTop;
|
|
2744
2837
|
var height = yBot - yTop;
|
|
@@ -2988,38 +3081,38 @@
|
|
|
2988
3081
|
};
|
|
2989
3082
|
|
|
2990
3083
|
/**
|
|
2991
|
-
* Returns the area of a single segment
|
|
3084
|
+
* Returns the area of a single cubic-bezier segment.
|
|
2992
3085
|
*
|
|
2993
3086
|
* http://objectmix.com/graphics/133553-area-closed-bezier-curve.html
|
|
2994
3087
|
*
|
|
2995
|
-
* @param {number}
|
|
2996
|
-
* @param {number}
|
|
2997
|
-
* @param {number}
|
|
2998
|
-
* @param {number}
|
|
2999
|
-
* @param {number}
|
|
3000
|
-
* @param {number}
|
|
3001
|
-
* @param {number}
|
|
3002
|
-
* @param {number}
|
|
3088
|
+
* @param {number} x1 the starting point X
|
|
3089
|
+
* @param {number} y1 the starting point Y
|
|
3090
|
+
* @param {number} c1x the first control point X
|
|
3091
|
+
* @param {number} c1y the first control point Y
|
|
3092
|
+
* @param {number} c2x the second control point X
|
|
3093
|
+
* @param {number} c2y the second control point Y
|
|
3094
|
+
* @param {number} x2 the ending point X
|
|
3095
|
+
* @param {number} y2 the ending point Y
|
|
3003
3096
|
* @returns {number} the area of the cubic-bezier segment
|
|
3004
3097
|
*/
|
|
3005
|
-
function getCubicSegArea(
|
|
3006
|
-
return (3 * ((
|
|
3007
|
-
+ (
|
|
3008
|
-
+ (
|
|
3098
|
+
function getCubicSegArea(x1, y1, c1x, c1y, c2x, c2y, x2, y2) {
|
|
3099
|
+
return (3 * ((y2 - y1) * (c1x + c2x) - (x2 - x1) * (c1y + c2y)
|
|
3100
|
+
+ (c1y * (x1 - c2x)) - (c1x * (y1 - c2y))
|
|
3101
|
+
+ (y2 * (c2x + x1 / 3)) - (x2 * (c2y + y1 / 3)))) / 20;
|
|
3009
3102
|
}
|
|
3010
3103
|
|
|
3011
3104
|
/**
|
|
3012
3105
|
* Returns the area of a shape.
|
|
3013
3106
|
* @author Jürg Lehni & Jonathan Puckey
|
|
3014
3107
|
*
|
|
3015
|
-
*
|
|
3108
|
+
* @see https://github.com/paperjs/paper.js/blob/develop/src/path/Path.js
|
|
3016
3109
|
*
|
|
3017
3110
|
* @param {SVGPathCommander.pathArray} path the shape `pathArray`
|
|
3018
3111
|
* @returns {number} the length of the cubic-bezier segment
|
|
3019
3112
|
*/
|
|
3020
3113
|
function getPathArea(path) {
|
|
3021
|
-
var x = 0; var y = 0;
|
|
3022
|
-
|
|
3114
|
+
var x = 0; var y = 0; var len = 0;
|
|
3115
|
+
|
|
3023
3116
|
return pathToCurve(path).map(function (seg) {
|
|
3024
3117
|
var assign, assign$1;
|
|
3025
3118
|
|
|
@@ -3030,85 +3123,255 @@
|
|
|
3030
3123
|
default:
|
|
3031
3124
|
// @ts-ignore -- the utility will have proper amount of params
|
|
3032
3125
|
len = getCubicSegArea.apply(void 0, [ x, y ].concat( seg.slice(1) ));
|
|
3033
|
-
|
|
3034
|
-
(assign$1 = seg.slice(-2)
|
|
3126
|
+
// @ts-ignore -- the segment always has numbers
|
|
3127
|
+
(assign$1 = seg.slice(-2), x = assign$1[0], y = assign$1[1]);
|
|
3035
3128
|
return len;
|
|
3036
3129
|
}
|
|
3037
3130
|
}).reduce(function (a, b) { return a + b; }, 0);
|
|
3038
3131
|
}
|
|
3039
3132
|
|
|
3040
3133
|
/**
|
|
3041
|
-
*
|
|
3042
|
-
*
|
|
3043
|
-
*
|
|
3044
|
-
*
|
|
3134
|
+
* Returns the shape total length, or the equivalent to `shape.getTotalLength()`.
|
|
3135
|
+
*
|
|
3136
|
+
* This is the `pathToCurve` version which is faster and more efficient for
|
|
3137
|
+
* paths that are `curveArray`.
|
|
3138
|
+
*
|
|
3139
|
+
* @param {string | SVGPathCommander.curveArray} path the target `pathArray`
|
|
3140
|
+
* @returns {number} the `curveArray` total length
|
|
3141
|
+
*/
|
|
3142
|
+
function getPathLength(path) {
|
|
3143
|
+
var totalLength = 0;
|
|
3144
|
+
pathToCurve(path).forEach(function (s, i, curveArray) {
|
|
3145
|
+
var args = s[0] !== 'M' ? curveArray[i - 1].slice(-2).concat( s.slice(1)) : [];
|
|
3146
|
+
// @ts-ignore
|
|
3147
|
+
totalLength += s[0] === 'M' ? 0 : segmentCubicFactory.apply(void 0, args);
|
|
3148
|
+
});
|
|
3149
|
+
return totalLength;
|
|
3150
|
+
}
|
|
3151
|
+
|
|
3152
|
+
/**
|
|
3153
|
+
* Returns the length of a A (arc-to) segment,
|
|
3154
|
+
* or an {x,y} point at a given length.
|
|
3155
|
+
*
|
|
3156
|
+
* @param {number} X1 the starting x position
|
|
3157
|
+
* @param {number} Y1 the starting y position
|
|
3158
|
+
* @param {number} RX x-radius of the arc
|
|
3159
|
+
* @param {number} RY y-radius of the arc
|
|
3160
|
+
* @param {number} angle x-axis-rotation of the arc
|
|
3161
|
+
* @param {number} LAF large-arc-flag of the arc
|
|
3162
|
+
* @param {number} SF sweep-flag of the arc
|
|
3163
|
+
* @param {number} X2 the ending x position
|
|
3164
|
+
* @param {number} Y2 the ending y position
|
|
3165
|
+
* @param {number} distance the point distance
|
|
3166
|
+
* @returns {{x: number, y: number} | number} the segment length or point
|
|
3167
|
+
*/
|
|
3168
|
+
function segmentArcFactory(X1, Y1, RX, RY, angle, LAF, SF, X2, Y2, distance) {
|
|
3169
|
+
var assign;
|
|
3170
|
+
|
|
3171
|
+
var ref = [X1, Y1];
|
|
3172
|
+
var x1 = ref[0];
|
|
3173
|
+
var y1 = ref[1];
|
|
3174
|
+
var cubicSeg = arcToCubic(X1, Y1, RX, RY, angle, LAF, SF, X2, Y2);
|
|
3175
|
+
var totalLength = 0;
|
|
3176
|
+
var cubicSubseg = [];
|
|
3177
|
+
var argsc = [];
|
|
3178
|
+
var segLen = 0;
|
|
3179
|
+
|
|
3180
|
+
for (var i = 0, ii = cubicSeg.length; i < ii; i += 6) {
|
|
3181
|
+
cubicSubseg = cubicSeg.slice(i, i + 6);
|
|
3182
|
+
argsc = [x1, y1 ].concat( cubicSubseg);
|
|
3183
|
+
// @ts-ignore
|
|
3184
|
+
segLen = segmentCubicFactory.apply(void 0, argsc);
|
|
3185
|
+
if (typeof distance === 'number' && totalLength + segLen >= distance) {
|
|
3186
|
+
// @ts-ignore -- this is a `cubicSegment`
|
|
3187
|
+
return segmentCubicFactory.apply(void 0, argsc.concat( [distance - totalLength] ));
|
|
3188
|
+
}
|
|
3189
|
+
totalLength += segLen;
|
|
3190
|
+
(assign = cubicSubseg.slice(-2), x1 = assign[0], y1 = assign[1]);
|
|
3191
|
+
}
|
|
3192
|
+
|
|
3193
|
+
return totalLength;
|
|
3194
|
+
}
|
|
3195
|
+
|
|
3196
|
+
/**
|
|
3197
|
+
* Returns the {x,y} coordinates of a point at a
|
|
3198
|
+
* given length of a quad-bezier segment.
|
|
3199
|
+
*
|
|
3200
|
+
* @see https://github.com/substack/point-at-length
|
|
3201
|
+
*
|
|
3202
|
+
* @param {number} x1 the starting point X
|
|
3203
|
+
* @param {number} y1 the starting point Y
|
|
3204
|
+
* @param {number} cx the control point X
|
|
3205
|
+
* @param {number} cy the control point Y
|
|
3206
|
+
* @param {number} x2 the ending point X
|
|
3207
|
+
* @param {number} y2 the ending point Y
|
|
3045
3208
|
* @param {number} t a [0-1] ratio
|
|
3046
|
-
* @returns {number}
|
|
3209
|
+
* @returns {{x: number, y: number}} the requested {x,y} coordinates
|
|
3047
3210
|
*/
|
|
3048
|
-
function
|
|
3049
|
-
var t1 =
|
|
3050
|
-
|
|
3051
|
-
|
|
3211
|
+
function getPointAtQuadSegmentLength(x1, y1, cx, cy, x2, y2, t) {
|
|
3212
|
+
var t1 = 1 - t;
|
|
3213
|
+
return {
|
|
3214
|
+
x: (Math.pow( t1, 2 )) * x1
|
|
3215
|
+
+ 2 * t1 * t * cx
|
|
3216
|
+
+ (Math.pow( t, 2 )) * x2,
|
|
3217
|
+
y: (Math.pow( t1, 2 )) * y1
|
|
3218
|
+
+ 2 * t1 * t * cy
|
|
3219
|
+
+ (Math.pow( t, 2 )) * y2,
|
|
3220
|
+
};
|
|
3052
3221
|
}
|
|
3053
3222
|
|
|
3054
3223
|
/**
|
|
3055
|
-
* Returns the
|
|
3224
|
+
* Returns the Q (quadratic-bezier) segment length,
|
|
3225
|
+
* or an {x,y} point at a given length.
|
|
3056
3226
|
*
|
|
3057
3227
|
* @param {number} x1 the starting point X
|
|
3058
3228
|
* @param {number} y1 the starting point Y
|
|
3059
|
-
* @param {number}
|
|
3060
|
-
* @param {number}
|
|
3061
|
-
* @param {number}
|
|
3062
|
-
* @param {number}
|
|
3063
|
-
* @param {number}
|
|
3064
|
-
* @
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
var
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
var
|
|
3077
|
-
var
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3229
|
+
* @param {number} qx the control point X
|
|
3230
|
+
* @param {number} qy the control point Y
|
|
3231
|
+
* @param {number} x2 the ending point X
|
|
3232
|
+
* @param {number} y2 the ending point Y
|
|
3233
|
+
* @param {number=} distance the distance to point
|
|
3234
|
+
* @returns {{x: number, y: number} | number} the segment length or point
|
|
3235
|
+
*/
|
|
3236
|
+
function segmentQuadFactory(x1, y1, qx, qy, x2, y2, distance) {
|
|
3237
|
+
var assign;
|
|
3238
|
+
|
|
3239
|
+
var x = x1; var y = y1;
|
|
3240
|
+
var totalLength = 0;
|
|
3241
|
+
var prev = [x1, y1, totalLength];
|
|
3242
|
+
/** @type {[number, number]} */
|
|
3243
|
+
var cur = [x1, y1];
|
|
3244
|
+
var t = 0;
|
|
3245
|
+
|
|
3246
|
+
var n = 101;
|
|
3247
|
+
for (var j = 0; j <= n; j += 1) {
|
|
3248
|
+
t = j / n;
|
|
3249
|
+
|
|
3250
|
+
((assign = getPointAtQuadSegmentLength(x1, y1, qx, qy, x2, y2, t), x = assign.x, y = assign.y));
|
|
3251
|
+
totalLength += distanceSquareRoot(cur, [x, y]);
|
|
3252
|
+
cur = [x, y];
|
|
3253
|
+
|
|
3254
|
+
if (typeof distance === 'number' && totalLength >= distance) {
|
|
3255
|
+
var dv = (totalLength - distance) / (totalLength - prev[2]);
|
|
3256
|
+
|
|
3257
|
+
return {
|
|
3258
|
+
x: cur[0] * (1 - dv) + prev[0] * dv,
|
|
3259
|
+
y: cur[1] * (1 - dv) + prev[1] * dv,
|
|
3260
|
+
};
|
|
3261
|
+
}
|
|
3262
|
+
prev = [x, y, totalLength];
|
|
3263
|
+
}
|
|
3264
|
+
return totalLength;
|
|
3091
3265
|
}
|
|
3092
3266
|
|
|
3093
3267
|
/**
|
|
3094
|
-
* Returns the shape total length
|
|
3095
|
-
* or the equivalent to `shape.getTotalLength()`
|
|
3096
|
-
* pathToCurve version
|
|
3268
|
+
* Returns a {x,y} point at a given length of a shape or the shape total length.
|
|
3097
3269
|
*
|
|
3098
|
-
* @param {SVGPathCommander.pathArray}
|
|
3099
|
-
* @
|
|
3270
|
+
* @param {string | SVGPathCommander.pathArray} pathInput the `pathArray` to look into
|
|
3271
|
+
* @param {number=} distance the length of the shape to look at
|
|
3272
|
+
* @returns {{x: number, y: number} | number} the total length or point
|
|
3100
3273
|
*/
|
|
3101
|
-
function
|
|
3274
|
+
function pathLengthFactory(pathInput, distance) {
|
|
3275
|
+
var assign, assign$1, assign$2;
|
|
3276
|
+
|
|
3102
3277
|
var totalLength = 0;
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3278
|
+
var isM = true;
|
|
3279
|
+
/** @type {number[]} */
|
|
3280
|
+
var data = [];
|
|
3281
|
+
var pathCommand = 'M';
|
|
3282
|
+
var segLen = 0;
|
|
3283
|
+
var x = 0;
|
|
3284
|
+
var y = 0;
|
|
3285
|
+
var mx = 0;
|
|
3286
|
+
var my = 0;
|
|
3287
|
+
var seg;
|
|
3288
|
+
var path = fixPath(normalizePath(pathInput));
|
|
3289
|
+
|
|
3290
|
+
for (var i = 0, ll = path.length; i < ll; i += 1) {
|
|
3291
|
+
seg = path[i];
|
|
3292
|
+
(assign = seg, pathCommand = assign[0]);
|
|
3293
|
+
isM = pathCommand === 'M';
|
|
3294
|
+
// @ts-ignore
|
|
3295
|
+
data = !isM ? [x, y ].concat( seg.slice(1)) : data;
|
|
3296
|
+
|
|
3297
|
+
// this segment is always ZERO
|
|
3298
|
+
if (isM) {
|
|
3299
|
+
// remember mx, my for Z
|
|
3106
3300
|
// @ts-ignore
|
|
3107
|
-
|
|
3108
|
-
|
|
3301
|
+
(assign$1 = seg, mx = assign$1[1], my = assign$1[2]);
|
|
3302
|
+
if (typeof distance === 'number' && distance < 0.001) {
|
|
3303
|
+
return { x: mx, y: my };
|
|
3304
|
+
}
|
|
3305
|
+
} else if (pathCommand === 'L') {
|
|
3306
|
+
// @ts-ignore
|
|
3307
|
+
segLen = segmentLineFactory.apply(void 0, data);
|
|
3308
|
+
if (distance && totalLength + segLen >= distance) {
|
|
3309
|
+
// @ts-ignore
|
|
3310
|
+
return segmentLineFactory.apply(void 0, data.concat( [distance - totalLength] ));
|
|
3311
|
+
}
|
|
3312
|
+
totalLength += segLen;
|
|
3313
|
+
} else if (pathCommand === 'A') {
|
|
3314
|
+
// @ts-ignore
|
|
3315
|
+
segLen = segmentArcFactory.apply(void 0, data);
|
|
3316
|
+
if (distance && totalLength + segLen >= distance) {
|
|
3317
|
+
// @ts-ignore
|
|
3318
|
+
return segmentArcFactory.apply(void 0, data.concat( [distance - totalLength] ));
|
|
3319
|
+
}
|
|
3320
|
+
totalLength += segLen;
|
|
3321
|
+
} else if (pathCommand === 'C') {
|
|
3322
|
+
// @ts-ignore
|
|
3323
|
+
segLen = segmentCubicFactory.apply(void 0, data);
|
|
3324
|
+
if (distance && totalLength + segLen >= distance) {
|
|
3325
|
+
// @ts-ignore
|
|
3326
|
+
return segmentCubicFactory.apply(void 0, data.concat( [distance - totalLength] ));
|
|
3327
|
+
}
|
|
3328
|
+
totalLength += segLen;
|
|
3329
|
+
} else if (pathCommand === 'Q') {
|
|
3330
|
+
// @ts-ignore
|
|
3331
|
+
segLen = segmentQuadFactory.apply(void 0, data);
|
|
3332
|
+
if (distance && totalLength + segLen >= distance) {
|
|
3333
|
+
// @ts-ignore
|
|
3334
|
+
return segmentQuadFactory.apply(void 0, data.concat( [distance - totalLength] ));
|
|
3335
|
+
}
|
|
3336
|
+
totalLength += segLen;
|
|
3337
|
+
} else if (pathCommand === 'Z') {
|
|
3338
|
+
data = [x, y, mx, my];
|
|
3339
|
+
// @ts-ignore
|
|
3340
|
+
segLen = segmentLineFactory.apply(void 0, data);
|
|
3341
|
+
if (distance && totalLength + segLen >= distance) {
|
|
3342
|
+
// @ts-ignore
|
|
3343
|
+
return segmentLineFactory.apply(void 0, data.concat( [distance - totalLength] ));
|
|
3344
|
+
}
|
|
3345
|
+
totalLength += segLen;
|
|
3346
|
+
}
|
|
3347
|
+
|
|
3348
|
+
// @ts-ignore -- needed for the below
|
|
3349
|
+
(assign$2 = pathCommand !== 'Z' ? seg.slice(-2) : [mx, my], x = assign$2[0], y = assign$2[1]);
|
|
3350
|
+
}
|
|
3351
|
+
|
|
3352
|
+
// native `getPointAtLength` behavior when the given distance
|
|
3353
|
+
// is higher than total length
|
|
3354
|
+
if (distance && distance >= totalLength) {
|
|
3355
|
+
return { x: x, y: y };
|
|
3356
|
+
}
|
|
3357
|
+
|
|
3109
3358
|
return totalLength;
|
|
3110
3359
|
}
|
|
3111
3360
|
|
|
3361
|
+
/**
|
|
3362
|
+
* Returns the shape total length, or the equivalent to `shape.getTotalLength()`.
|
|
3363
|
+
*
|
|
3364
|
+
* The `normalizePath` version is lighter, faster, more efficient and more accurate
|
|
3365
|
+
* with paths that are not `curveArray`.
|
|
3366
|
+
*
|
|
3367
|
+
* @param {string | SVGPathCommander.pathArray} pathInput the target `pathArray`
|
|
3368
|
+
* @returns {number} the shape total length
|
|
3369
|
+
*/
|
|
3370
|
+
function getTotalLength(pathInput) {
|
|
3371
|
+
// @ts-ignore - it's fine
|
|
3372
|
+
return pathLengthFactory(pathInput);
|
|
3373
|
+
}
|
|
3374
|
+
|
|
3112
3375
|
/**
|
|
3113
3376
|
* Check if a path is drawn clockwise and returns true if so,
|
|
3114
3377
|
* false otherwise.
|
|
@@ -3123,34 +3386,244 @@
|
|
|
3123
3386
|
/**
|
|
3124
3387
|
* Returns [x,y] coordinates of a point at a given length of a shape.
|
|
3125
3388
|
*
|
|
3126
|
-
* @param {string | SVGPathCommander.pathArray}
|
|
3389
|
+
* @param {string | SVGPathCommander.pathArray} pathInput the `pathArray` to look into
|
|
3390
|
+
* @param {number} distance the length of the shape to look at
|
|
3391
|
+
* @returns {{x: number, y: number}} the requested {x, y} point coordinates
|
|
3392
|
+
*/
|
|
3393
|
+
function getPointAtLength(pathInput, distance) {
|
|
3394
|
+
// @ts-ignore
|
|
3395
|
+
return pathLengthFactory(pathInput, distance);
|
|
3396
|
+
}
|
|
3397
|
+
|
|
3398
|
+
/**
|
|
3399
|
+
* Returns [x,y] coordinates of a point at a given length of a shape.
|
|
3400
|
+
* `pathToCurve` version.
|
|
3401
|
+
*
|
|
3402
|
+
* @param {string | SVGPathCommander.pathArray} pathInput the `pathArray` to look into
|
|
3127
3403
|
* @param {number} length the length of the shape to look at
|
|
3128
|
-
* @returns {number
|
|
3404
|
+
* @returns {{x: number, y: number}} the requested {x, y} point coordinates
|
|
3129
3405
|
*/
|
|
3130
|
-
function
|
|
3406
|
+
function getPointAtPathLength(pathInput, length) {
|
|
3407
|
+
var assign, assign$1;
|
|
3408
|
+
|
|
3131
3409
|
var totalLength = 0;
|
|
3132
|
-
var
|
|
3410
|
+
var isM = true;
|
|
3411
|
+
var segLen = 0;
|
|
3133
3412
|
var data;
|
|
3134
|
-
var result;
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3413
|
+
var result = { x: 0, y: 0 };
|
|
3414
|
+
var x = 0;
|
|
3415
|
+
var y = 0;
|
|
3416
|
+
var path = pathToCurve(pathInput);
|
|
3417
|
+
var seg;
|
|
3418
|
+
|
|
3419
|
+
for (var i = 0, l = path.length; i < l; i += 1) {
|
|
3420
|
+
seg = path[i];
|
|
3421
|
+
isM = seg[0] === 'M';
|
|
3422
|
+
data = !isM ? [x, y ].concat( seg.slice(1)) : seg.slice(1);
|
|
3138
3423
|
// @ts-ignore
|
|
3139
|
-
segLen =
|
|
3424
|
+
segLen = !isM ? segmentCubicFactory.apply(void 0, data) : 0;
|
|
3425
|
+
|
|
3426
|
+
if (isM) {
|
|
3427
|
+
(assign = seg, x = assign[1], y = assign[2]);
|
|
3428
|
+
} else if (totalLength + segLen > length) {
|
|
3429
|
+
var args = data.concat( [length - totalLength]);
|
|
3430
|
+
|
|
3431
|
+
// @ts-ignore -- `args` values are correct
|
|
3432
|
+
result = segmentCubicFactory.apply(void 0, args);
|
|
3433
|
+
break;
|
|
3434
|
+
}
|
|
3140
3435
|
totalLength += segLen;
|
|
3436
|
+
// @ts-ignore - these are usually numbers
|
|
3437
|
+
(assign$1 = seg.slice(-2), x = assign$1[0], y = assign$1[1]);
|
|
3438
|
+
}
|
|
3439
|
+
return result;
|
|
3440
|
+
}
|
|
3141
3441
|
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3442
|
+
/**
|
|
3443
|
+
* Returns the properties at a given length in path.
|
|
3444
|
+
*
|
|
3445
|
+
* @param {string | SVGPathCommander.pathArray} pathInput target `pathArray`
|
|
3446
|
+
* @param {number=} distance the given length
|
|
3447
|
+
* @returns {SVGPathCommander.segmentProperties=} the requested properties
|
|
3448
|
+
*/
|
|
3449
|
+
function getPropertiesAtLength(pathInput, distance) {
|
|
3450
|
+
var pathArray = parsePathString(pathInput);
|
|
3451
|
+
var segments = [];
|
|
3452
|
+
|
|
3453
|
+
var pathTemp = [].concat( pathArray );
|
|
3454
|
+
// @ts-ignore
|
|
3455
|
+
var pathLength = getTotalLength(pathTemp);
|
|
3456
|
+
var index = pathTemp.length - 1;
|
|
3457
|
+
var lengthAtSegment = 0;
|
|
3458
|
+
var length = 0;
|
|
3459
|
+
/** @type {SVGPathCommander.pathSegment} */
|
|
3460
|
+
var segment = pathArray[0];
|
|
3461
|
+
var ref = segment.slice(-2);
|
|
3462
|
+
var x = ref[0];
|
|
3463
|
+
var y = ref[1];
|
|
3464
|
+
var point = { x: x, y: y };
|
|
3465
|
+
|
|
3466
|
+
// If the path is empty, return 0.
|
|
3467
|
+
if (index <= 0 || !distance || !Number.isFinite(distance)) {
|
|
3468
|
+
return {
|
|
3469
|
+
// @ts-ignore
|
|
3470
|
+
segment: segment, index: 0, length: length, point: point, lengthAtSegment: lengthAtSegment,
|
|
3471
|
+
};
|
|
3472
|
+
}
|
|
3473
|
+
|
|
3474
|
+
if (distance >= pathLength) {
|
|
3475
|
+
pathTemp = pathArray.slice(0, -1);
|
|
3476
|
+
// @ts-ignore
|
|
3477
|
+
lengthAtSegment = getTotalLength(pathTemp);
|
|
3478
|
+
// @ts-ignore
|
|
3479
|
+
length = pathLength - lengthAtSegment;
|
|
3480
|
+
return {
|
|
3146
3481
|
// @ts-ignore
|
|
3147
|
-
|
|
3482
|
+
segment: pathArray[index], index: index, length: length, lengthAtSegment: lengthAtSegment,
|
|
3483
|
+
};
|
|
3484
|
+
}
|
|
3485
|
+
|
|
3486
|
+
while (index > 0) {
|
|
3487
|
+
segment = pathTemp[index];
|
|
3488
|
+
pathTemp = pathTemp.slice(0, -1);
|
|
3489
|
+
// @ts-ignore -- `pathTemp` === `pathSegment[]` === `pathArray`
|
|
3490
|
+
lengthAtSegment = getTotalLength(pathTemp);
|
|
3491
|
+
// @ts-ignore
|
|
3492
|
+
length = pathLength - lengthAtSegment;
|
|
3493
|
+
pathLength = lengthAtSegment;
|
|
3494
|
+
segments.push({
|
|
3495
|
+
segment: segment, index: index, length: length, lengthAtSegment: lengthAtSegment,
|
|
3496
|
+
});
|
|
3497
|
+
index -= 1;
|
|
3498
|
+
}
|
|
3499
|
+
|
|
3500
|
+
// @ts-ignore
|
|
3501
|
+
return segments.find(function (ref) {
|
|
3502
|
+
var l = ref.lengthAtSegment;
|
|
3503
|
+
|
|
3504
|
+
return l <= distance;
|
|
3505
|
+
});
|
|
3506
|
+
}
|
|
3507
|
+
|
|
3508
|
+
/**
|
|
3509
|
+
* Returns the point in path closest to a given point.
|
|
3510
|
+
* @see https://bl.ocks.org/mbostock/8027637
|
|
3511
|
+
*
|
|
3512
|
+
* @param {string | SVGPathCommander.pathArray} pathInput target `pathArray`
|
|
3513
|
+
* @param {{x: number, y: number}} point the given point
|
|
3514
|
+
* @returns {SVGPathCommander.pointProperties} the requested properties
|
|
3515
|
+
*/
|
|
3516
|
+
function getPropertiesAtPoint(pathInput, point) {
|
|
3517
|
+
var path = fixPath(parsePathString(pathInput));
|
|
3518
|
+
var normalPath = normalizePath(path);
|
|
3519
|
+
var pathLength = getTotalLength(path);
|
|
3520
|
+
/** @param {{x: number, y: number}} p */
|
|
3521
|
+
var distanceTo = function (p) {
|
|
3522
|
+
var dx = p.x - point.x;
|
|
3523
|
+
var dy = p.y - point.y;
|
|
3524
|
+
return dx * dx + dy * dy;
|
|
3525
|
+
};
|
|
3526
|
+
var precision = 8;
|
|
3527
|
+
var scan = { x: 0, y: 0 };
|
|
3528
|
+
var scanDistance = 0;
|
|
3529
|
+
var closest = scan;
|
|
3530
|
+
var bestLength = 0;
|
|
3531
|
+
var bestDistance = Infinity;
|
|
3532
|
+
|
|
3533
|
+
// linear scan for coarse approximation
|
|
3534
|
+
for (var scanLength = 0; scanLength <= pathLength; scanLength += precision) {
|
|
3535
|
+
scan = getPointAtLength(normalPath, scanLength);
|
|
3536
|
+
scanDistance = distanceTo(scan);
|
|
3537
|
+
if (scanDistance < bestDistance) {
|
|
3538
|
+
closest = scan;
|
|
3539
|
+
bestLength = scanLength;
|
|
3540
|
+
bestDistance = scanDistance;
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3543
|
+
|
|
3544
|
+
// binary search for precise estimate
|
|
3545
|
+
precision /= 2;
|
|
3546
|
+
var before = { x: 0, y: 0 };
|
|
3547
|
+
var after = before;
|
|
3548
|
+
var beforeLength = 0;
|
|
3549
|
+
var afterLength = 0;
|
|
3550
|
+
var beforeDistance = 0;
|
|
3551
|
+
var afterDistance = 0;
|
|
3552
|
+
|
|
3553
|
+
while (precision > 0.5) {
|
|
3554
|
+
beforeLength = bestLength - precision;
|
|
3555
|
+
before = getPointAtLength(normalPath, beforeLength);
|
|
3556
|
+
beforeDistance = distanceTo(before);
|
|
3557
|
+
afterLength = bestLength + precision;
|
|
3558
|
+
after = getPointAtLength(normalPath, afterLength);
|
|
3559
|
+
afterDistance = distanceTo(after);
|
|
3560
|
+
if (beforeLength >= 0 && beforeDistance < bestDistance) {
|
|
3561
|
+
closest = before;
|
|
3562
|
+
bestLength = beforeLength;
|
|
3563
|
+
bestDistance = beforeDistance;
|
|
3564
|
+
} else if (afterLength <= pathLength && afterDistance < bestDistance) {
|
|
3565
|
+
closest = after;
|
|
3566
|
+
bestLength = afterLength;
|
|
3567
|
+
bestDistance = afterDistance;
|
|
3148
3568
|
} else {
|
|
3149
|
-
|
|
3569
|
+
precision /= 2;
|
|
3150
3570
|
}
|
|
3571
|
+
}
|
|
3151
3572
|
|
|
3152
|
-
|
|
3153
|
-
|
|
3573
|
+
var segment = getPropertiesAtLength(path, bestDistance);
|
|
3574
|
+
var distance = Math.sqrt(bestDistance);
|
|
3575
|
+
|
|
3576
|
+
return { closest: closest, distance: distance, segment: segment };
|
|
3577
|
+
}
|
|
3578
|
+
|
|
3579
|
+
/**
|
|
3580
|
+
* Returns the point in path closest to a given point.
|
|
3581
|
+
*
|
|
3582
|
+
* @param {string | SVGPathCommander.pathArray} pathInput target `pathArray`
|
|
3583
|
+
* @param {{x: number, y: number}} point the given point
|
|
3584
|
+
* @returns {{x: number, y: number}} the best match
|
|
3585
|
+
*/
|
|
3586
|
+
function getClosestPoint(pathInput, point) {
|
|
3587
|
+
return getPropertiesAtPoint(pathInput, point).closest;
|
|
3588
|
+
}
|
|
3589
|
+
|
|
3590
|
+
/**
|
|
3591
|
+
* Returns the path segment which contains a given point.
|
|
3592
|
+
*
|
|
3593
|
+
* @param {string | SVGPathCommander.pathArray} path the `pathArray` to look into
|
|
3594
|
+
* @param {{x: number, y: number}} point the point of the shape to look for
|
|
3595
|
+
* @returns {SVGPathCommander.pathSegment?} the requested segment
|
|
3596
|
+
*/
|
|
3597
|
+
function getSegmentOfPoint(path, point) {
|
|
3598
|
+
var props = getPropertiesAtPoint(path, point);
|
|
3599
|
+
var segment = props.segment;
|
|
3600
|
+
return typeof segment !== 'undefined' ? segment.segment : null;
|
|
3601
|
+
}
|
|
3602
|
+
|
|
3603
|
+
/**
|
|
3604
|
+
* Returns the segment at a given length.
|
|
3605
|
+
* @param {string | SVGPathCommander.pathArray} pathInput the target `pathArray`
|
|
3606
|
+
* @param {number} distance the distance in path to look at
|
|
3607
|
+
* @returns {SVGPathCommander.pathSegment?} the requested segment
|
|
3608
|
+
*/
|
|
3609
|
+
function getSegmentAtLength(pathInput, distance) {
|
|
3610
|
+
var props = getPropertiesAtLength(pathInput, distance);
|
|
3611
|
+
var ref = typeof props !== 'undefined' ? props : { segment: null };
|
|
3612
|
+
var segment = ref.segment;
|
|
3613
|
+
return segment;
|
|
3614
|
+
}
|
|
3615
|
+
|
|
3616
|
+
/**
|
|
3617
|
+
* Checks if a given point is in the stroke of a path.
|
|
3618
|
+
*
|
|
3619
|
+
* @param {string | SVGPathCommander.pathArray} pathInput target path
|
|
3620
|
+
* @param {{x: number, y: number}} point
|
|
3621
|
+
* @returns {boolean} the query result
|
|
3622
|
+
*/
|
|
3623
|
+
function isPointInStroke(pathInput, point) {
|
|
3624
|
+
var ref = getPropertiesAtPoint(pathInput, point);
|
|
3625
|
+
var distance = ref.distance;
|
|
3626
|
+
return Math.abs(distance) < 0.01;
|
|
3154
3627
|
}
|
|
3155
3628
|
|
|
3156
3629
|
/**
|
|
@@ -3406,8 +3879,16 @@
|
|
|
3406
3879
|
getDrawDirection: getDrawDirection,
|
|
3407
3880
|
getPathArea: getPathArea,
|
|
3408
3881
|
getPathBBox: getPathBBox,
|
|
3882
|
+
getTotalLength: getTotalLength,
|
|
3409
3883
|
getPathLength: getPathLength,
|
|
3410
3884
|
getPointAtLength: getPointAtLength,
|
|
3885
|
+
getPointAtPathLength: getPointAtPathLength,
|
|
3886
|
+
getClosestPoint: getClosestPoint,
|
|
3887
|
+
getSegmentOfPoint: getSegmentOfPoint,
|
|
3888
|
+
getPropertiesAtPoint: getPropertiesAtPoint,
|
|
3889
|
+
getPropertiesAtLength: getPropertiesAtLength,
|
|
3890
|
+
getSegmentAtLength: getSegmentAtLength,
|
|
3891
|
+
isPointInStroke: isPointInStroke,
|
|
3411
3892
|
clonePath: clonePath,
|
|
3412
3893
|
splitPath: splitPath,
|
|
3413
3894
|
fixPath: fixPath,
|
|
@@ -3421,7 +3902,7 @@
|
|
|
3421
3902
|
options: defaultOptions,
|
|
3422
3903
|
};
|
|
3423
3904
|
|
|
3424
|
-
var version = "0.1.
|
|
3905
|
+
var version = "0.1.22";
|
|
3425
3906
|
|
|
3426
3907
|
// @ts-ignore
|
|
3427
3908
|
|