svg-path-commander 0.1.25 → 0.2.0-alpha3
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 +5 -3
- package/dist/svg-path-commander.es5.js +3948 -0
- package/dist/svg-path-commander.es5.min.js +2 -0
- package/dist/svg-path-commander.esm.js +425 -404
- package/dist/svg-path-commander.esm.min.js +2 -2
- package/dist/svg-path-commander.js +1432 -1513
- package/dist/svg-path-commander.min.js +2 -2
- package/package.json +28 -12
- package/src/convert/pathToAbsolute.js +4 -4
- package/src/convert/pathToCurve.js +2 -2
- package/src/convert/pathToRelative.js +4 -4
- package/src/convert/pathToString.js +2 -2
- package/src/math/polygonLength.js +1 -2
- package/src/options/options.js +1 -1
- package/src/parser/error.js +2 -0
- package/src/parser/finalizeSegment.js +1 -1
- package/src/parser/paramsParser.js +1 -1
- package/src/parser/parsePathString.js +6 -11
- package/src/parser/pathParser.js +1 -1
- package/src/parser/scanFlag.js +3 -3
- package/src/parser/scanParam.js +7 -6
- package/src/parser/scanSegment.js +3 -2
- package/src/parser/skipSpaces.js +1 -1
- package/src/process/clonePath.js +1 -1
- package/src/process/fixArc.js +1 -1
- package/src/process/fixPath.js +2 -2
- package/src/process/getSVGMatrix.js +3 -2
- package/src/process/normalizePath.js +3 -3
- package/src/process/normalizeSegment.js +2 -2
- package/src/process/optimizePath.js +2 -2
- package/src/process/projection2d.js +2 -2
- package/src/process/reverseCurve.js +3 -3
- package/src/process/reversePath.js +4 -5
- package/src/process/roundPath.js +7 -6
- package/src/process/segmentToCubic.js +3 -3
- package/src/process/shortenSegment.js +3 -3
- package/src/process/splitCubic.js +1 -1
- package/src/process/splitPath.js +1 -1
- package/src/process/transformPath.js +8 -7
- package/src/svg-path-commander.js +60 -20
- package/src/util/getClosestPoint.js +1 -1
- package/src/util/getCubicSize.js +1 -1
- package/src/util/getDrawDirection.js +1 -1
- package/src/util/getPathArea.js +1 -1
- package/src/util/getPathBBox.js +2 -2
- package/src/util/getPathLength.js +1 -1
- package/src/util/getPointAtLength.js +1 -1
- package/src/util/getPropertiesAtLength.js +3 -3
- package/src/util/getPropertiesAtPoint.js +2 -2
- package/src/util/getSegmentAtLength.js +2 -2
- package/src/util/getSegmentOfPoint.js +2 -2
- package/src/util/getTotalLength.js +1 -1
- package/src/util/isAbsoluteArray.js +1 -1
- package/src/util/isCurveArray.js +1 -1
- package/src/util/isNormalizedArray.js +1 -1
- package/src/util/isPathArray.js +1 -1
- package/src/util/isPointInStroke.js +2 -2
- package/src/util/isRelativeArray.js +1 -1
- package/src/util/pathLengthFactory.js +3 -2
- package/src/util/segmentArcFactory.js +1 -1
- package/src/util/segmentCubicFactory.js +1 -1
- package/src/util/segmentLineFactory.js +1 -1
- package/src/util/segmentQuadFactory.js +1 -1
- package/src/util/shapeToPath.js +13 -13
- package/src/util/util.js +0 -2
- package/types/index.d.ts +1 -3
- package/types/more/modules.ts +0 -3
- package/types/more/svg.d.ts +11 -10
- package/types/svg-path-commander.d.ts +214 -226
- package/src/util/createPath.js +0 -17
- package/src/util/getPointAtPathLength.js +0 -15
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* SVGPathCommander v0.
|
|
2
|
+
* SVGPathCommander v0.2.0alpha3 (http://thednp.github.io/svg-path-commander)
|
|
3
3
|
* Copyright 2022 © thednp
|
|
4
4
|
* Licensed under MIT (https://github.com/thednp/svg-path-commander/blob/master/LICENSE)
|
|
5
5
|
*/
|
|
6
6
|
/**
|
|
7
7
|
* SVGPathCommander default options
|
|
8
|
-
* @type {
|
|
8
|
+
* @type {SVGPath.options}
|
|
9
9
|
*/
|
|
10
10
|
const defaultOptions = {
|
|
11
11
|
origin: [0, 0, 0],
|
|
@@ -23,7 +23,7 @@ const paramsCount = {
|
|
|
23
23
|
/**
|
|
24
24
|
* Breaks the parsing of a pathString once a segment is finalized.
|
|
25
25
|
*
|
|
26
|
-
* @param {
|
|
26
|
+
* @param {SVGPath.PathParser} path the `PathParser` instance
|
|
27
27
|
*/
|
|
28
28
|
function finalizeSegment(path) {
|
|
29
29
|
let pathCommand = path.pathValue[path.segmentStart];
|
|
@@ -51,13 +51,13 @@ function finalizeSegment(path) {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
const
|
|
54
|
+
const error = 'SVGPathCommander error';
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* Validates an A (arc-to) specific path command value.
|
|
58
58
|
* Usually a `large-arc-flag` or `sweep-flag`.
|
|
59
59
|
*
|
|
60
|
-
* @param {
|
|
60
|
+
* @param {SVGPath.PathParser} path the `PathParser` instance
|
|
61
61
|
*/
|
|
62
62
|
function scanFlag(path) {
|
|
63
63
|
const { index } = path;
|
|
@@ -75,7 +75,7 @@ function scanFlag(path) {
|
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
path.err = `${
|
|
78
|
+
path.err = `${error}: invalid Arc flag "${ch}", expecting 0 or 1 at index ${index}`;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
/**
|
|
@@ -88,11 +88,13 @@ function isDigit(code) {
|
|
|
88
88
|
return (code >= 48 && code <= 57); // 0..9
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
const invalidPathValue = 'Invalid path value';
|
|
92
|
+
|
|
91
93
|
/**
|
|
92
94
|
* Validates every character of the path string,
|
|
93
95
|
* every path command, negative numbers or floating point numbers.
|
|
94
96
|
*
|
|
95
|
-
* @param {
|
|
97
|
+
* @param {SVGPath.PathParser} path the `PathParser` instance
|
|
96
98
|
*/
|
|
97
99
|
function scanParam(path) {
|
|
98
100
|
const { max, pathValue, index: start } = path;
|
|
@@ -105,7 +107,7 @@ function scanParam(path) {
|
|
|
105
107
|
|
|
106
108
|
if (index >= max) {
|
|
107
109
|
// path.err = 'SvgPath: missed param (at pos ' + index + ')';
|
|
108
|
-
path.err = `${invalidPathValue} at ${index}
|
|
110
|
+
path.err = `${error}: ${invalidPathValue} at index ${index}, "pathValue" is missing param`;
|
|
109
111
|
return;
|
|
110
112
|
}
|
|
111
113
|
ch = pathValue.charCodeAt(index);
|
|
@@ -119,7 +121,7 @@ function scanParam(path) {
|
|
|
119
121
|
// https://github.com/ariya/esprimas
|
|
120
122
|
if (!isDigit(ch) && ch !== 0x2E/* . */) {
|
|
121
123
|
// path.err = 'SvgPath: param should start with 0..9 or `.` (at pos ' + index + ')';
|
|
122
|
-
path.err = `${invalidPathValue} at index ${index}
|
|
124
|
+
path.err = `${error}: ${invalidPathValue} at index ${index}, "${pathValue[index]}" is not a number`;
|
|
123
125
|
return;
|
|
124
126
|
}
|
|
125
127
|
|
|
@@ -134,7 +136,7 @@ function scanParam(path) {
|
|
|
134
136
|
if (ch && isDigit(ch)) {
|
|
135
137
|
// path.err = 'SvgPath: numbers started with `0` such as `09`
|
|
136
138
|
// are illegal (at pos ' + start + ')';
|
|
137
|
-
path.err = `${invalidPathValue} at index ${start}
|
|
139
|
+
path.err = `${error}: ${invalidPathValue} at index ${start}, "${pathValue[start]}" illegal number`;
|
|
138
140
|
return;
|
|
139
141
|
}
|
|
140
142
|
}
|
|
@@ -158,7 +160,7 @@ function scanParam(path) {
|
|
|
158
160
|
|
|
159
161
|
if (ch === 0x65/* e */ || ch === 0x45/* E */) {
|
|
160
162
|
if (hasDot && !hasCeiling && !hasDecimal) {
|
|
161
|
-
path.err = `${invalidPathValue} at index ${index}
|
|
163
|
+
path.err = `${error}: ${invalidPathValue} at index ${index}, "${pathValue[index]}" invalid float exponent`;
|
|
162
164
|
return;
|
|
163
165
|
}
|
|
164
166
|
|
|
@@ -174,7 +176,7 @@ function scanParam(path) {
|
|
|
174
176
|
}
|
|
175
177
|
} else {
|
|
176
178
|
// path.err = 'SvgPath: invalid float exponent (at pos ' + index + ')';
|
|
177
|
-
path.err = `${invalidPathValue} at index ${index}: ${pathValue[index]} invalid float exponent`;
|
|
179
|
+
path.err = `${error}: ${invalidPathValue} at index ${index}: "${pathValue[index]}" invalid float exponent`;
|
|
178
180
|
return;
|
|
179
181
|
}
|
|
180
182
|
}
|
|
@@ -204,7 +206,7 @@ function isSpace(ch) {
|
|
|
204
206
|
* path string every time it encounters any kind of
|
|
205
207
|
* space character.
|
|
206
208
|
*
|
|
207
|
-
* @param {
|
|
209
|
+
* @param {SVGPath.PathParser} path the `PathParser` instance
|
|
208
210
|
*/
|
|
209
211
|
function skipSpaces(path) {
|
|
210
212
|
const { pathValue, max } = path;
|
|
@@ -268,7 +270,7 @@ function isArcCommand(code) {
|
|
|
268
270
|
* Scans every character in the path string to determine
|
|
269
271
|
* where a segment starts and where it ends.
|
|
270
272
|
*
|
|
271
|
-
* @param {
|
|
273
|
+
* @param {SVGPath.PathParser} path the `PathParser` instance
|
|
272
274
|
*/
|
|
273
275
|
function scanSegment(path) {
|
|
274
276
|
const { max, pathValue, index } = path;
|
|
@@ -278,7 +280,7 @@ function scanSegment(path) {
|
|
|
278
280
|
path.segmentStart = index;
|
|
279
281
|
|
|
280
282
|
if (!isPathCommand(cmdCode)) {
|
|
281
|
-
path.err = `${
|
|
283
|
+
path.err = `${error}: ${invalidPathValue} "${pathValue[index]}" is not a path command`;
|
|
282
284
|
return;
|
|
283
285
|
}
|
|
284
286
|
|
|
@@ -328,7 +330,7 @@ function scanSegment(path) {
|
|
|
328
330
|
/**
|
|
329
331
|
* Returns a clone of an existing `pathArray`.
|
|
330
332
|
*
|
|
331
|
-
* @param {
|
|
333
|
+
* @param {SVGPath.pathArray | SVGPath.pathSegment} path the source `pathArray`
|
|
332
334
|
* @returns {any} the cloned `pathArray`
|
|
333
335
|
*/
|
|
334
336
|
function clonePath(path) {
|
|
@@ -342,7 +344,7 @@ function clonePath(path) {
|
|
|
342
344
|
* @param {string} pathString
|
|
343
345
|
*/
|
|
344
346
|
function PathParser(pathString) {
|
|
345
|
-
/** @type {
|
|
347
|
+
/** @type {SVGPath.pathArray} */
|
|
346
348
|
// @ts-ignore
|
|
347
349
|
this.segments = [];
|
|
348
350
|
/** @type {string} */
|
|
@@ -364,7 +366,7 @@ function PathParser(pathString) {
|
|
|
364
366
|
/**
|
|
365
367
|
* Iterates an array to check if it's an actual `pathArray`.
|
|
366
368
|
*
|
|
367
|
-
* @param {string |
|
|
369
|
+
* @param {string | SVGPath.pathArray} path the `pathArray` to be checked
|
|
368
370
|
* @returns {boolean} iteration result
|
|
369
371
|
*/
|
|
370
372
|
function isPathArray(path) {
|
|
@@ -378,8 +380,8 @@ function isPathArray(path) {
|
|
|
378
380
|
* Parses a path string value and returns an array
|
|
379
381
|
* of segments we like to call `pathArray`.
|
|
380
382
|
*
|
|
381
|
-
* @param {
|
|
382
|
-
* @returns {
|
|
383
|
+
* @param {SVGPath.pathArray | string} pathInput the string to be parsed
|
|
384
|
+
* @returns {SVGPath.pathArray | string} the resulted `pathArray`
|
|
383
385
|
*/
|
|
384
386
|
function parsePathString(pathInput) {
|
|
385
387
|
if (isPathArray(pathInput)) {
|
|
@@ -396,27 +398,22 @@ function parsePathString(pathInput) {
|
|
|
396
398
|
scanSegment(path);
|
|
397
399
|
}
|
|
398
400
|
|
|
399
|
-
if (path.err.length) {
|
|
400
|
-
// @ts-ignore
|
|
401
|
-
path.segments = [];
|
|
402
|
-
} else if (path.segments.length) {
|
|
401
|
+
if (!path.err.length) {
|
|
403
402
|
if (!'mM'.includes(path.segments[0][0])) {
|
|
404
|
-
path.err = `${
|
|
405
|
-
// @ts-ignore
|
|
406
|
-
path.segments = [];
|
|
403
|
+
path.err = `${error}: missing M/m`;
|
|
407
404
|
} else {
|
|
408
405
|
path.segments[0][0] = 'M';
|
|
409
406
|
}
|
|
410
407
|
}
|
|
411
408
|
|
|
412
|
-
return path.segments;
|
|
409
|
+
return path.err ? path.err : path.segments;
|
|
413
410
|
}
|
|
414
411
|
|
|
415
412
|
/**
|
|
416
413
|
* Iterates an array to check if it's a `pathArray`
|
|
417
414
|
* with all absolute values.
|
|
418
415
|
*
|
|
419
|
-
* @param {string |
|
|
416
|
+
* @param {string | SVGPath.pathArray} path the `pathArray` to be checked
|
|
420
417
|
* @returns {boolean} iteration result
|
|
421
418
|
*/
|
|
422
419
|
function isAbsoluteArray(path) {
|
|
@@ -429,8 +426,8 @@ function isAbsoluteArray(path) {
|
|
|
429
426
|
* Parses a path string value or object and returns an array
|
|
430
427
|
* of segments, all converted to absolute values.
|
|
431
428
|
*
|
|
432
|
-
* @param {string |
|
|
433
|
-
* @returns {
|
|
429
|
+
* @param {string | SVGPath.pathArray} pathInput the path string | object
|
|
430
|
+
* @returns {SVGPath.absoluteArray} the resulted `pathArray` with absolute values
|
|
434
431
|
*/
|
|
435
432
|
function pathToAbsolute(pathInput) {
|
|
436
433
|
if (isAbsoluteArray(pathInput)) {
|
|
@@ -446,7 +443,7 @@ function pathToAbsolute(pathInput) {
|
|
|
446
443
|
return path.map((segment) => {
|
|
447
444
|
const values = segment.slice(1).map(Number);
|
|
448
445
|
const [pathCommand] = segment;
|
|
449
|
-
/** @type {
|
|
446
|
+
/** @type {SVGPath.absoluteCommand} */
|
|
450
447
|
// @ts-ignore
|
|
451
448
|
const absCommand = pathCommand.toUpperCase();
|
|
452
449
|
|
|
@@ -456,7 +453,7 @@ function pathToAbsolute(pathInput) {
|
|
|
456
453
|
my = y;
|
|
457
454
|
return ['M', x, y];
|
|
458
455
|
}
|
|
459
|
-
/** @type {
|
|
456
|
+
/** @type {SVGPath.absoluteSegment} */
|
|
460
457
|
// @ts-ignore
|
|
461
458
|
let absoluteSegment = [];
|
|
462
459
|
|
|
@@ -519,7 +516,7 @@ function pathToAbsolute(pathInput) {
|
|
|
519
516
|
* Iterates an array to check if it's a `pathArray`
|
|
520
517
|
* with relative values.
|
|
521
518
|
*
|
|
522
|
-
* @param {string |
|
|
519
|
+
* @param {string | SVGPath.pathArray} path the `pathArray` to be checked
|
|
523
520
|
* @returns {boolean} iteration result
|
|
524
521
|
*/
|
|
525
522
|
function isRelativeArray(path) {
|
|
@@ -532,8 +529,8 @@ function isRelativeArray(path) {
|
|
|
532
529
|
* Parses a path string value or object and returns an array
|
|
533
530
|
* of segments, all converted to relative values.
|
|
534
531
|
*
|
|
535
|
-
* @param {string |
|
|
536
|
-
* @returns {
|
|
532
|
+
* @param {string | SVGPath.pathArray} pathInput the path string | object
|
|
533
|
+
* @returns {SVGPath.relativeArray} the resulted `pathArray` with relative values
|
|
537
534
|
*/
|
|
538
535
|
function pathToRelative(pathInput) {
|
|
539
536
|
if (isRelativeArray(pathInput)) {
|
|
@@ -549,7 +546,7 @@ function pathToRelative(pathInput) {
|
|
|
549
546
|
return path.map((segment) => {
|
|
550
547
|
const values = segment.slice(1).map(Number);
|
|
551
548
|
const [pathCommand] = segment;
|
|
552
|
-
/** @type {
|
|
549
|
+
/** @type {SVGPath.relativeCommand} */
|
|
553
550
|
// @ts-ignore
|
|
554
551
|
const relativeCommand = pathCommand.toLowerCase();
|
|
555
552
|
|
|
@@ -560,7 +557,7 @@ function pathToRelative(pathInput) {
|
|
|
560
557
|
return ['M', x, y];
|
|
561
558
|
}
|
|
562
559
|
|
|
563
|
-
/** @type {
|
|
560
|
+
/** @type {SVGPath.relativeSegment} */
|
|
564
561
|
// @ts-ignore -- trust me DON'T CHANGE
|
|
565
562
|
let relativeSegment = [];
|
|
566
563
|
|
|
@@ -627,7 +624,7 @@ function pathToRelative(pathInput) {
|
|
|
627
624
|
/**
|
|
628
625
|
* Splits an extended A (arc-to) segment into two cubic-bezier segments.
|
|
629
626
|
*
|
|
630
|
-
* @param {
|
|
627
|
+
* @param {SVGPath.pathArray} path the `pathArray` this segment belongs to
|
|
631
628
|
* @param {string[]} allPathCommands all previous path commands
|
|
632
629
|
* @param {number} i the segment index
|
|
633
630
|
*/
|
|
@@ -684,10 +681,10 @@ function shorthandToCubic(x1, y1, x2, y2, prevCommand) {
|
|
|
684
681
|
/**
|
|
685
682
|
* Normalizes a single segment of a `pathArray` object.
|
|
686
683
|
*
|
|
687
|
-
* @param {
|
|
684
|
+
* @param {SVGPath.pathSegment} segment the segment object
|
|
688
685
|
* @param {any} params the coordinates of the previous segment
|
|
689
686
|
* @param {string} prevCommand the path command of the previous segment
|
|
690
|
-
* @returns {
|
|
687
|
+
* @returns {SVGPath.normalSegment} the normalized segment
|
|
691
688
|
*/
|
|
692
689
|
function normalizeSegment(segment, params, prevCommand) {
|
|
693
690
|
const [pathCommand] = segment;
|
|
@@ -734,7 +731,7 @@ function normalizeSegment(segment, params, prevCommand) {
|
|
|
734
731
|
* with all segments are in non-shorthand notation
|
|
735
732
|
* with absolute values.
|
|
736
733
|
*
|
|
737
|
-
* @param {string |
|
|
734
|
+
* @param {string | SVGPath.pathArray} path the `pathArray` to be checked
|
|
738
735
|
* @returns {boolean} iteration result
|
|
739
736
|
*/
|
|
740
737
|
function isNormalizedArray(path) {
|
|
@@ -743,7 +740,7 @@ function isNormalizedArray(path) {
|
|
|
743
740
|
}
|
|
744
741
|
|
|
745
742
|
/**
|
|
746
|
-
* @type {
|
|
743
|
+
* @type {SVGPath.parserParams}
|
|
747
744
|
*/
|
|
748
745
|
const paramsParser = {
|
|
749
746
|
x1: 0, y1: 0, x2: 0, y2: 0, x: 0, y: 0, qx: null, qy: null,
|
|
@@ -754,8 +751,8 @@ const paramsParser = {
|
|
|
754
751
|
* * convert segments to absolute values
|
|
755
752
|
* * convert shorthand path commands to their non-shorthand notation
|
|
756
753
|
*
|
|
757
|
-
* @param {string |
|
|
758
|
-
* @returns {
|
|
754
|
+
* @param {string | SVGPath.pathArray} pathInput the string to be parsed or 'pathArray'
|
|
755
|
+
* @returns {SVGPath.normalArray} the normalized `pathArray`
|
|
759
756
|
*/
|
|
760
757
|
function normalizePath(pathInput) {
|
|
761
758
|
if (isNormalizedArray(pathInput)) {
|
|
@@ -763,7 +760,7 @@ function normalizePath(pathInput) {
|
|
|
763
760
|
return clonePath(pathInput);
|
|
764
761
|
}
|
|
765
762
|
|
|
766
|
-
/** @type {
|
|
763
|
+
/** @type {SVGPath.normalArray} */
|
|
767
764
|
// @ts-ignore -- `absoluteArray` will become a `normalArray`
|
|
768
765
|
const path = pathToAbsolute(pathInput);
|
|
769
766
|
const params = { ...paramsParser };
|
|
@@ -804,8 +801,8 @@ function normalizePath(pathInput) {
|
|
|
804
801
|
* use `splitPath` first and apply this utility on each
|
|
805
802
|
* sub-path separately.
|
|
806
803
|
*
|
|
807
|
-
* @param {
|
|
808
|
-
* @return {
|
|
804
|
+
* @param {SVGPath.pathArray | string} pathInput the `pathArray` source
|
|
805
|
+
* @return {SVGPath.pathArray} a fixed `pathArray`
|
|
809
806
|
*/
|
|
810
807
|
function fixPath(pathInput) {
|
|
811
808
|
const pathArray = parsePathString(pathInput);
|
|
@@ -828,7 +825,7 @@ function fixPath(pathInput) {
|
|
|
828
825
|
* Iterates an array to check if it's a `pathArray`
|
|
829
826
|
* with all C (cubic bezier) segments.
|
|
830
827
|
*
|
|
831
|
-
* @param {string |
|
|
828
|
+
* @param {string | SVGPath.pathArray} path the `Array` to be checked
|
|
832
829
|
* @returns {boolean} iteration result
|
|
833
830
|
*/
|
|
834
831
|
function isCurveArray(path) {
|
|
@@ -1017,7 +1014,7 @@ function distanceSquareRoot(a, b) {
|
|
|
1017
1014
|
}
|
|
1018
1015
|
|
|
1019
1016
|
/**
|
|
1020
|
-
* Returns the length of a line (L,V,H,Z) segment
|
|
1017
|
+
* Returns the length of a line (L,V,H,Z) segment
|
|
1021
1018
|
* or a point at a given length.
|
|
1022
1019
|
*
|
|
1023
1020
|
* @param {number} x1 the starting point X
|
|
@@ -1078,9 +1075,9 @@ function lineToCubic(x1, y1, x2, y2) {
|
|
|
1078
1075
|
/**
|
|
1079
1076
|
* Converts any segment to C (cubic-bezier).
|
|
1080
1077
|
*
|
|
1081
|
-
* @param {
|
|
1082
|
-
* @param {
|
|
1083
|
-
* @returns {
|
|
1078
|
+
* @param {SVGPath.pathSegment} segment the source segment
|
|
1079
|
+
* @param {SVGPath.parserParams} params the source segment parameters
|
|
1080
|
+
* @returns {SVGPath.cubicSegment | SVGPath.MSegment} the cubic-bezier segment
|
|
1084
1081
|
*/
|
|
1085
1082
|
function segmentToCubic(segment, params) {
|
|
1086
1083
|
const [pathCommand] = segment;
|
|
@@ -1129,8 +1126,8 @@ function segmentToCubic(segment, params) {
|
|
|
1129
1126
|
* In addition, un-necessary `Z` segment is removed if previous segment
|
|
1130
1127
|
* extends to the `M` segment.
|
|
1131
1128
|
*
|
|
1132
|
-
* @param {string |
|
|
1133
|
-
* @returns {
|
|
1129
|
+
* @param {string | SVGPath.pathArray} pathInput the string to be parsed or 'pathArray'
|
|
1130
|
+
* @returns {SVGPath.curveArray} the resulted `pathArray` converted to cubic-bezier
|
|
1134
1131
|
*/
|
|
1135
1132
|
function pathToCurve(pathInput) {
|
|
1136
1133
|
if (isCurveArray(pathInput)) {
|
|
@@ -1169,23 +1166,24 @@ function pathToCurve(pathInput) {
|
|
|
1169
1166
|
* Rounds the values of a `pathArray` instance to
|
|
1170
1167
|
* a specified amount of decimals and returns it.
|
|
1171
1168
|
*
|
|
1172
|
-
* @param {
|
|
1173
|
-
* @param {number |
|
|
1174
|
-
* @returns {
|
|
1169
|
+
* @param {SVGPath.pathArray} path the source `pathArray`
|
|
1170
|
+
* @param {number | false} roundOption the amount of decimals to round numbers to
|
|
1171
|
+
* @returns {SVGPath.pathArray} the resulted `pathArray` with rounded values
|
|
1175
1172
|
*/
|
|
1176
1173
|
function roundPath(path, roundOption) {
|
|
1177
1174
|
let { round } = defaultOptions;
|
|
1178
1175
|
if (roundOption === false || round === false) return clonePath(path);
|
|
1179
|
-
round = roundOption >= 1 ? roundOption : round;
|
|
1176
|
+
// round = roundOption >= 1 ? roundOption : round;
|
|
1177
|
+
// allow for ZERO decimals
|
|
1178
|
+
round = roundOption >= 0 ? roundOption : round;
|
|
1180
1179
|
// to round values to the power
|
|
1181
1180
|
// the `round` value must be integer
|
|
1182
|
-
// @ts-ignore
|
|
1183
1181
|
const pow = round >= 1 ? (10 ** round) : 1;
|
|
1184
1182
|
|
|
1185
1183
|
// @ts-ignore -- `pathSegment[]` is `pathArray`
|
|
1186
1184
|
return path.map((pi) => {
|
|
1187
1185
|
const values = pi.slice(1).map(Number)
|
|
1188
|
-
.map((n) => (
|
|
1186
|
+
.map((n) => (round ? (Math.round(n * pow) / pow) : Math.round(n)));
|
|
1189
1187
|
return [pi[0], ...values];
|
|
1190
1188
|
});
|
|
1191
1189
|
}
|
|
@@ -1194,8 +1192,8 @@ function roundPath(path, roundOption) {
|
|
|
1194
1192
|
* Returns a valid `d` attribute string value created
|
|
1195
1193
|
* by rounding values and concatenating the `pathArray` segments.
|
|
1196
1194
|
*
|
|
1197
|
-
* @param {
|
|
1198
|
-
* @param {
|
|
1195
|
+
* @param {SVGPath.pathArray} path the `pathArray` object
|
|
1196
|
+
* @param {number | false} round amount of decimals to round values to
|
|
1199
1197
|
* @returns {string} the concatenated path string
|
|
1200
1198
|
*/
|
|
1201
1199
|
function pathToString(path, round) {
|
|
@@ -1204,11 +1202,10 @@ function pathToString(path, round) {
|
|
|
1204
1202
|
}
|
|
1205
1203
|
|
|
1206
1204
|
/**
|
|
1207
|
-
* Reverses all segments
|
|
1208
|
-
* and returns a new instance.
|
|
1205
|
+
* Reverses all segments of a `pathArray` and returns a new `pathArray` instance.
|
|
1209
1206
|
*
|
|
1210
|
-
* @param {
|
|
1211
|
-
* @returns {
|
|
1207
|
+
* @param {SVGPath.pathArray} pathInput the source `pathArray`
|
|
1208
|
+
* @returns {SVGPath.pathArray} the reversed `pathArray`
|
|
1212
1209
|
*/
|
|
1213
1210
|
function reversePath(pathInput) {
|
|
1214
1211
|
const absolutePath = pathToAbsolute(pathInput);
|
|
@@ -1233,7 +1230,7 @@ function reversePath(pathInput) {
|
|
|
1233
1230
|
/** @type {number} */
|
|
1234
1231
|
const x = i ? path[i - 1].x : path[pLen - 1].x;
|
|
1235
1232
|
const y = i ? path[i - 1].y : path[pLen - 1].y;
|
|
1236
|
-
/** @type {
|
|
1233
|
+
/** @type {SVGPath.pathSegment} */
|
|
1237
1234
|
// @ts-ignore
|
|
1238
1235
|
let result = [];
|
|
1239
1236
|
|
|
@@ -1307,7 +1304,7 @@ function reversePath(pathInput) {
|
|
|
1307
1304
|
* In the process, values are converted to absolute
|
|
1308
1305
|
* for visual consistency.
|
|
1309
1306
|
*
|
|
1310
|
-
* @param {
|
|
1307
|
+
* @param {SVGPath.pathArray | string} pathInput the source `pathArray`
|
|
1311
1308
|
* @return {string[]} an array with all sub-path strings
|
|
1312
1309
|
*/
|
|
1313
1310
|
function splitPath(pathInput) {
|
|
@@ -1321,11 +1318,11 @@ function splitPath(pathInput) {
|
|
|
1321
1318
|
/**
|
|
1322
1319
|
* Shorten a single segment of a `pathArray` object.
|
|
1323
1320
|
*
|
|
1324
|
-
* @param {
|
|
1325
|
-
* @param {
|
|
1321
|
+
* @param {SVGPath.absoluteSegment} segment the `absoluteSegment` object
|
|
1322
|
+
* @param {SVGPath.normalSegment} normalSegment the `normalSegment` object
|
|
1326
1323
|
* @param {any} params the coordinates of the previous segment
|
|
1327
1324
|
* @param {string} prevCommand the path command of the previous segment
|
|
1328
|
-
* @returns {
|
|
1325
|
+
* @returns {SVGPath.shortSegment | SVGPath.pathSegment} the shortened segment
|
|
1329
1326
|
*/
|
|
1330
1327
|
function shortenSegment(segment, normalSegment, params, prevCommand) {
|
|
1331
1328
|
const [pathCommand] = segment;
|
|
@@ -1389,9 +1386,9 @@ function shortenSegment(segment, normalSegment, params, prevCommand) {
|
|
|
1389
1386
|
* * implement `auto` for rounding values based on pathBBox
|
|
1390
1387
|
* * also revers path check if it's smaller string, maybe?
|
|
1391
1388
|
*
|
|
1392
|
-
* @param {
|
|
1389
|
+
* @param {SVGPath.pathArray} pathInput a string or `pathArray`
|
|
1393
1390
|
* @param {number | boolean} round the amount of decimals to round values to
|
|
1394
|
-
* @returns {
|
|
1391
|
+
* @returns {SVGPath.pathArray} the optimized `pathArray`
|
|
1395
1392
|
*/
|
|
1396
1393
|
function optimizePath(pathInput, round) {
|
|
1397
1394
|
const path = pathToAbsolute(pathInput);
|
|
@@ -2277,14 +2274,15 @@ Object.assign(CSSMatrix, { Version: Version$1 });
|
|
|
2277
2274
|
/**
|
|
2278
2275
|
* Returns a transformation matrix to apply to `<path>` elements.
|
|
2279
2276
|
*
|
|
2280
|
-
* @see
|
|
2277
|
+
* @see SVGPath.transformObject
|
|
2281
2278
|
*
|
|
2282
|
-
* @param {
|
|
2279
|
+
* @param {SVGPath.transformObject} transform the `transformObject`
|
|
2283
2280
|
* @returns {CSSMatrix} a new transformation matrix
|
|
2284
2281
|
*/
|
|
2285
2282
|
function getSVGMatrix(transform) {
|
|
2286
2283
|
let matrix = new CSSMatrix();
|
|
2287
2284
|
const { origin } = transform;
|
|
2285
|
+
// @ts-ignore -- at this point the origin is surely defined
|
|
2288
2286
|
const [originX, originY] = origin;
|
|
2289
2287
|
const { translate } = transform;
|
|
2290
2288
|
const { rotate } = transform;
|
|
@@ -2415,9 +2413,9 @@ function transformEllipse(m, rx, ry, ax) {
|
|
|
2415
2413
|
* Details =>
|
|
2416
2414
|
* https://stackoverflow.com/questions/23792505/predicted-rendering-of-css-3d-transformed-pixel
|
|
2417
2415
|
*
|
|
2418
|
-
* @param {
|
|
2416
|
+
* @param {SVGPath.CSSMatrix} m the transformation matrix
|
|
2419
2417
|
* @param {[number, number]} point2D the initial [x,y] coordinates
|
|
2420
|
-
* @param {number[]} origin the
|
|
2418
|
+
* @param {number[]} origin the [x,y,z] transform origin
|
|
2421
2419
|
* @returns {[number, number]} the projected [x,y] coordinates
|
|
2422
2420
|
*/
|
|
2423
2421
|
function projection2d(m, point2D, origin) {
|
|
@@ -2443,9 +2441,9 @@ function projection2d(m, point2D, origin) {
|
|
|
2443
2441
|
* Since *SVGElement* doesn't support 3D transformation, this function
|
|
2444
2442
|
* creates a 2D projection of the <path> element.
|
|
2445
2443
|
*
|
|
2446
|
-
* @param {
|
|
2447
|
-
* @param {
|
|
2448
|
-
* @returns {
|
|
2444
|
+
* @param {SVGPath.pathArray} path the `pathArray` to apply transformation
|
|
2445
|
+
* @param {SVGPath.transformObject} transform the transform functions `Object`
|
|
2446
|
+
* @returns {SVGPath.pathArray} the resulted `pathArray`
|
|
2449
2447
|
*/
|
|
2450
2448
|
function transformPath(path, transform) {
|
|
2451
2449
|
let x = 0; let y = 0; let i; let j; let ii; let jj; let lx; let ly; let te;
|
|
@@ -2460,12 +2458,12 @@ function transformPath(path, transform) {
|
|
|
2460
2458
|
const matrix2d = [a, b, c, d, e, f];
|
|
2461
2459
|
const params = { ...paramsParser };
|
|
2462
2460
|
/** @ts-ignore */
|
|
2463
|
-
/** @type {
|
|
2461
|
+
/** @type {SVGPath.pathSegment} */
|
|
2464
2462
|
// @ts-ignore
|
|
2465
2463
|
let segment = [];
|
|
2466
2464
|
let seglen = 0;
|
|
2467
2465
|
let pathCommand = '';
|
|
2468
|
-
/** @type {
|
|
2466
|
+
/** @type {SVGPath.pathTransformList[]} */
|
|
2469
2467
|
let transformedPath = [];
|
|
2470
2468
|
const allPathCommands = []; // needed for arc to curve transformation
|
|
2471
2469
|
|
|
@@ -2504,7 +2502,7 @@ function transformPath(path, transform) {
|
|
|
2504
2502
|
params.x2 = +(segment[seglen - 4]) || params.x1;
|
|
2505
2503
|
params.y2 = +(segment[seglen - 3]) || params.y1;
|
|
2506
2504
|
|
|
2507
|
-
/** @type {
|
|
2505
|
+
/** @type {SVGPath.pathTransformList} */
|
|
2508
2506
|
const result = {
|
|
2509
2507
|
s: absolutePath[i], c: absolutePath[i][0], x: params.x1, y: params.y1,
|
|
2510
2508
|
};
|
|
@@ -2541,6 +2539,7 @@ function transformPath(path, transform) {
|
|
|
2541
2539
|
case 'L':
|
|
2542
2540
|
case 'H':
|
|
2543
2541
|
case 'V':
|
|
2542
|
+
// @ts-ignore
|
|
2544
2543
|
[lx, ly] = projection2d(matrixInstance, [seg.x, seg.y], origin);
|
|
2545
2544
|
|
|
2546
2545
|
if (x !== lx && y !== ly) {
|
|
@@ -2557,7 +2556,7 @@ function transformPath(path, transform) {
|
|
|
2557
2556
|
default:
|
|
2558
2557
|
|
|
2559
2558
|
for (j = 1, jj = segment.length; j < jj; j += 2) {
|
|
2560
|
-
// compute line coordinates without altering previous coordinates
|
|
2559
|
+
// @ts-ignore compute line coordinates without altering previous coordinates
|
|
2561
2560
|
[x, y] = projection2d(matrixInstance, [+segment[j], +segment[j + 1]], origin);
|
|
2562
2561
|
segment[j] = x;
|
|
2563
2562
|
segment[j + 1] = y;
|
|
@@ -2599,7 +2598,7 @@ function getPointAtCubicSegmentLength(x1, y1, c1x, c1y, c2x, c2y, x2, y2, t) {
|
|
|
2599
2598
|
}
|
|
2600
2599
|
|
|
2601
2600
|
/**
|
|
2602
|
-
* Returns the length of a C (cubic-bezier) segment
|
|
2601
|
+
* Returns the length of a C (cubic-bezier) segment
|
|
2603
2602
|
* or an {x,y} point at a given length.
|
|
2604
2603
|
*
|
|
2605
2604
|
* @param {number} x1 the starting point X
|
|
@@ -2663,7 +2662,7 @@ function segmentCubicFactory(x1, y1, c1x, c1y, c2x, c2y, x2, y2, distance) {
|
|
|
2663
2662
|
* @param {number} c2y the second control point Y
|
|
2664
2663
|
* @param {number} x2 the ending point X
|
|
2665
2664
|
* @param {number} y2 the ending point Y
|
|
2666
|
-
* @returns {
|
|
2665
|
+
* @returns {SVGPath.segmentLimits} the bounding box of the cubic-bezier segment
|
|
2667
2666
|
*/
|
|
2668
2667
|
function getCubicSize(x1, y1, c1x, c1y, c2x, c2y, x2, y2) {
|
|
2669
2668
|
let a = (c2x - 2 * c1x + x1) - (x2 - 2 * c2x + c1x);
|
|
@@ -2720,8 +2719,8 @@ function getCubicSize(x1, y1, c1x, c1y, c2x, c2y, x2, y2) {
|
|
|
2720
2719
|
/**
|
|
2721
2720
|
* Returns the bounding box of a shape.
|
|
2722
2721
|
*
|
|
2723
|
-
* @param {
|
|
2724
|
-
* @returns {
|
|
2722
|
+
* @param {SVGPath.pathArray} path the shape `pathArray`
|
|
2723
|
+
* @returns {SVGPath.pathBBox} the length of the cubic-bezier segment
|
|
2725
2724
|
*/
|
|
2726
2725
|
function getPathBBox(path) {
|
|
2727
2726
|
if (!path) {
|
|
@@ -2778,6 +2777,254 @@ function getPathBBox(path) {
|
|
|
2778
2777
|
};
|
|
2779
2778
|
}
|
|
2780
2779
|
|
|
2780
|
+
/**
|
|
2781
|
+
* Returns the length of an A (arc-to) segment
|
|
2782
|
+
* or an {x,y} point at a given length.
|
|
2783
|
+
*
|
|
2784
|
+
* @param {number} X1 the starting x position
|
|
2785
|
+
* @param {number} Y1 the starting y position
|
|
2786
|
+
* @param {number} RX x-radius of the arc
|
|
2787
|
+
* @param {number} RY y-radius of the arc
|
|
2788
|
+
* @param {number} angle x-axis-rotation of the arc
|
|
2789
|
+
* @param {number} LAF large-arc-flag of the arc
|
|
2790
|
+
* @param {number} SF sweep-flag of the arc
|
|
2791
|
+
* @param {number} X2 the ending x position
|
|
2792
|
+
* @param {number} Y2 the ending y position
|
|
2793
|
+
* @param {number} distance the point distance
|
|
2794
|
+
* @returns {{x: number, y: number} | number} the segment length or point
|
|
2795
|
+
*/
|
|
2796
|
+
function segmentArcFactory(X1, Y1, RX, RY, angle, LAF, SF, X2, Y2, distance) {
|
|
2797
|
+
const cubicSeg = arcToCubic(X1, Y1, RX, RY, angle, LAF, SF, X2, Y2);
|
|
2798
|
+
const distanceIsNumber = typeof distance === 'number';
|
|
2799
|
+
let [x, y] = [X1, Y1];
|
|
2800
|
+
const lengthMargin = 0.001;
|
|
2801
|
+
let totalLength = 0;
|
|
2802
|
+
let cubicSubseg = [];
|
|
2803
|
+
let argsc = [];
|
|
2804
|
+
let segLen = 0;
|
|
2805
|
+
|
|
2806
|
+
if (distanceIsNumber && distance < lengthMargin) {
|
|
2807
|
+
return { x, y };
|
|
2808
|
+
}
|
|
2809
|
+
|
|
2810
|
+
for (let i = 0, ii = cubicSeg.length; i < ii; i += 6) {
|
|
2811
|
+
cubicSubseg = cubicSeg.slice(i, i + 6);
|
|
2812
|
+
argsc = [x, y, ...cubicSubseg];
|
|
2813
|
+
// @ts-ignore
|
|
2814
|
+
segLen = segmentCubicFactory(...argsc);
|
|
2815
|
+
if (distanceIsNumber && totalLength + segLen >= distance) {
|
|
2816
|
+
// @ts-ignore -- this is a `cubicSegment`
|
|
2817
|
+
return segmentCubicFactory(...argsc, distance - totalLength);
|
|
2818
|
+
}
|
|
2819
|
+
totalLength += segLen;
|
|
2820
|
+
[x, y] = cubicSubseg.slice(-2);
|
|
2821
|
+
}
|
|
2822
|
+
|
|
2823
|
+
if (distanceIsNumber && distance >= totalLength) {
|
|
2824
|
+
return { x: X2, y: Y2 };
|
|
2825
|
+
}
|
|
2826
|
+
|
|
2827
|
+
return totalLength;
|
|
2828
|
+
}
|
|
2829
|
+
|
|
2830
|
+
/**
|
|
2831
|
+
* Returns the {x,y} coordinates of a point at a
|
|
2832
|
+
* given length of a quad-bezier segment.
|
|
2833
|
+
*
|
|
2834
|
+
* @see https://github.com/substack/point-at-length
|
|
2835
|
+
*
|
|
2836
|
+
* @param {number} x1 the starting point X
|
|
2837
|
+
* @param {number} y1 the starting point Y
|
|
2838
|
+
* @param {number} cx the control point X
|
|
2839
|
+
* @param {number} cy the control point Y
|
|
2840
|
+
* @param {number} x2 the ending point X
|
|
2841
|
+
* @param {number} y2 the ending point Y
|
|
2842
|
+
* @param {number} t a [0-1] ratio
|
|
2843
|
+
* @returns {{x: number, y: number}} the requested {x,y} coordinates
|
|
2844
|
+
*/
|
|
2845
|
+
function getPointAtQuadSegmentLength(x1, y1, cx, cy, x2, y2, t) {
|
|
2846
|
+
const t1 = 1 - t;
|
|
2847
|
+
return {
|
|
2848
|
+
x: (t1 ** 2) * x1
|
|
2849
|
+
+ 2 * t1 * t * cx
|
|
2850
|
+
+ (t ** 2) * x2,
|
|
2851
|
+
y: (t1 ** 2) * y1
|
|
2852
|
+
+ 2 * t1 * t * cy
|
|
2853
|
+
+ (t ** 2) * y2,
|
|
2854
|
+
};
|
|
2855
|
+
}
|
|
2856
|
+
|
|
2857
|
+
/**
|
|
2858
|
+
* Returns the Q (quadratic-bezier) segment length
|
|
2859
|
+
* or an {x,y} point at a given length.
|
|
2860
|
+
*
|
|
2861
|
+
* @param {number} x1 the starting point X
|
|
2862
|
+
* @param {number} y1 the starting point Y
|
|
2863
|
+
* @param {number} qx the control point X
|
|
2864
|
+
* @param {number} qy the control point Y
|
|
2865
|
+
* @param {number} x2 the ending point X
|
|
2866
|
+
* @param {number} y2 the ending point Y
|
|
2867
|
+
* @param {number=} distance the distance to point
|
|
2868
|
+
* @returns {{x: number, y: number} | number} the segment length or point
|
|
2869
|
+
*/
|
|
2870
|
+
function segmentQuadFactory(x1, y1, qx, qy, x2, y2, distance) {
|
|
2871
|
+
const distanceIsNumber = typeof distance === 'number';
|
|
2872
|
+
const lengthMargin = 0.001;
|
|
2873
|
+
let x = x1; let y = y1;
|
|
2874
|
+
let totalLength = 0;
|
|
2875
|
+
let prev = [x1, y1, totalLength];
|
|
2876
|
+
/** @type {[number, number]} */
|
|
2877
|
+
let cur = [x1, y1];
|
|
2878
|
+
let t = 0;
|
|
2879
|
+
|
|
2880
|
+
if (distanceIsNumber && distance < lengthMargin) {
|
|
2881
|
+
return { x, y };
|
|
2882
|
+
}
|
|
2883
|
+
|
|
2884
|
+
const n = 100;
|
|
2885
|
+
for (let j = 0; j <= n; j += 1) {
|
|
2886
|
+
t = j / n;
|
|
2887
|
+
|
|
2888
|
+
({ x, y } = getPointAtQuadSegmentLength(x1, y1, qx, qy, x2, y2, t));
|
|
2889
|
+
totalLength += distanceSquareRoot(cur, [x, y]);
|
|
2890
|
+
cur = [x, y];
|
|
2891
|
+
|
|
2892
|
+
if (distanceIsNumber && totalLength >= distance) {
|
|
2893
|
+
const dv = (totalLength - distance) / (totalLength - prev[2]);
|
|
2894
|
+
|
|
2895
|
+
return {
|
|
2896
|
+
x: cur[0] * (1 - dv) + prev[0] * dv,
|
|
2897
|
+
y: cur[1] * (1 - dv) + prev[1] * dv,
|
|
2898
|
+
};
|
|
2899
|
+
}
|
|
2900
|
+
prev = [x, y, totalLength];
|
|
2901
|
+
}
|
|
2902
|
+
if (distanceIsNumber && distance >= totalLength) {
|
|
2903
|
+
return { x: x2, y: y2 };
|
|
2904
|
+
}
|
|
2905
|
+
return totalLength;
|
|
2906
|
+
}
|
|
2907
|
+
|
|
2908
|
+
/**
|
|
2909
|
+
* Returns a {x,y} point at a given length
|
|
2910
|
+
* of a shape or the shape total length.
|
|
2911
|
+
*
|
|
2912
|
+
* @param {string | SVGPath.pathArray} pathInput the `pathArray` to look into
|
|
2913
|
+
* @param {number=} distance the length of the shape to look at
|
|
2914
|
+
* @returns {{x: number, y: number} | number} the total length or point
|
|
2915
|
+
*/
|
|
2916
|
+
function pathLengthFactory(pathInput, distance) {
|
|
2917
|
+
const path = fixPath(normalizePath(pathInput));
|
|
2918
|
+
const distanceIsNumber = typeof distance === 'number';
|
|
2919
|
+
let totalLength = 0;
|
|
2920
|
+
let isM = true;
|
|
2921
|
+
/** @type {number[]} */
|
|
2922
|
+
let data = [];
|
|
2923
|
+
let pathCommand = 'M';
|
|
2924
|
+
let segLen = 0;
|
|
2925
|
+
let x = 0;
|
|
2926
|
+
let y = 0;
|
|
2927
|
+
let mx = 0;
|
|
2928
|
+
let my = 0;
|
|
2929
|
+
let seg;
|
|
2930
|
+
|
|
2931
|
+
for (let i = 0, ll = path.length; i < ll; i += 1) {
|
|
2932
|
+
seg = path[i];
|
|
2933
|
+
[pathCommand] = seg;
|
|
2934
|
+
isM = pathCommand === 'M';
|
|
2935
|
+
// @ts-ignore
|
|
2936
|
+
data = !isM ? [x, y, ...seg.slice(1)] : data;
|
|
2937
|
+
|
|
2938
|
+
// this segment is always ZERO
|
|
2939
|
+
if (isM) {
|
|
2940
|
+
// remember mx, my for Z
|
|
2941
|
+
// @ts-ignore
|
|
2942
|
+
[, mx, my] = seg;
|
|
2943
|
+
if (distanceIsNumber && distance < 0.001) {
|
|
2944
|
+
return { x: mx, y: my };
|
|
2945
|
+
}
|
|
2946
|
+
} else if (pathCommand === 'L') {
|
|
2947
|
+
// @ts-ignore
|
|
2948
|
+
segLen = segmentLineFactory(...data);
|
|
2949
|
+
if (distanceIsNumber && totalLength + segLen >= distance) {
|
|
2950
|
+
// @ts-ignore
|
|
2951
|
+
return segmentLineFactory(...data, distance - totalLength);
|
|
2952
|
+
}
|
|
2953
|
+
totalLength += segLen;
|
|
2954
|
+
} else if (pathCommand === 'A') {
|
|
2955
|
+
// @ts-ignore
|
|
2956
|
+
segLen = segmentArcFactory(...data);
|
|
2957
|
+
if (distanceIsNumber && totalLength + segLen >= distance) {
|
|
2958
|
+
// @ts-ignore
|
|
2959
|
+
return segmentArcFactory(...data, distance - totalLength);
|
|
2960
|
+
}
|
|
2961
|
+
totalLength += segLen;
|
|
2962
|
+
} else if (pathCommand === 'C') {
|
|
2963
|
+
// @ts-ignore
|
|
2964
|
+
segLen = segmentCubicFactory(...data);
|
|
2965
|
+
if (distanceIsNumber && totalLength + segLen >= distance) {
|
|
2966
|
+
// @ts-ignore
|
|
2967
|
+
return segmentCubicFactory(...data, distance - totalLength);
|
|
2968
|
+
}
|
|
2969
|
+
totalLength += segLen;
|
|
2970
|
+
} else if (pathCommand === 'Q') {
|
|
2971
|
+
// @ts-ignore
|
|
2972
|
+
segLen = segmentQuadFactory(...data);
|
|
2973
|
+
if (distanceIsNumber && totalLength + segLen >= distance) {
|
|
2974
|
+
// @ts-ignore
|
|
2975
|
+
return segmentQuadFactory(...data, distance - totalLength);
|
|
2976
|
+
}
|
|
2977
|
+
totalLength += segLen;
|
|
2978
|
+
} else if (pathCommand === 'Z') {
|
|
2979
|
+
data = [x, y, mx, my];
|
|
2980
|
+
// @ts-ignore
|
|
2981
|
+
segLen = segmentLineFactory(...data);
|
|
2982
|
+
if (distanceIsNumber && totalLength + segLen >= distance) {
|
|
2983
|
+
// @ts-ignore
|
|
2984
|
+
return segmentLineFactory(...data, distance - totalLength);
|
|
2985
|
+
}
|
|
2986
|
+
totalLength += segLen;
|
|
2987
|
+
}
|
|
2988
|
+
|
|
2989
|
+
// @ts-ignore -- needed for the below
|
|
2990
|
+
[x, y] = pathCommand !== 'Z' ? seg.slice(-2) : [mx, my];
|
|
2991
|
+
}
|
|
2992
|
+
|
|
2993
|
+
// native `getPointAtLength` behavior when the given distance
|
|
2994
|
+
// is higher than total length
|
|
2995
|
+
if (distanceIsNumber && distance >= totalLength) {
|
|
2996
|
+
return { x, y };
|
|
2997
|
+
}
|
|
2998
|
+
|
|
2999
|
+
return totalLength;
|
|
3000
|
+
}
|
|
3001
|
+
|
|
3002
|
+
/**
|
|
3003
|
+
* Returns the shape total length, or the equivalent to `shape.getTotalLength()`.
|
|
3004
|
+
*
|
|
3005
|
+
* The `normalizePath` version is lighter, faster, more efficient and more accurate
|
|
3006
|
+
* with paths that are not `curveArray`.
|
|
3007
|
+
*
|
|
3008
|
+
* @param {string | SVGPath.pathArray} pathInput the target `pathArray`
|
|
3009
|
+
* @returns {number} the shape total length
|
|
3010
|
+
*/
|
|
3011
|
+
function getTotalLength(pathInput) {
|
|
3012
|
+
// @ts-ignore - it's fine
|
|
3013
|
+
return pathLengthFactory(pathInput);
|
|
3014
|
+
}
|
|
3015
|
+
|
|
3016
|
+
/**
|
|
3017
|
+
* Returns [x,y] coordinates of a point at a given length of a shape.
|
|
3018
|
+
*
|
|
3019
|
+
* @param {string | SVGPath.pathArray} pathInput the `pathArray` to look into
|
|
3020
|
+
* @param {number} distance the length of the shape to look at
|
|
3021
|
+
* @returns {{x: number, y: number}} the requested {x, y} point coordinates
|
|
3022
|
+
*/
|
|
3023
|
+
function getPointAtLength(pathInput, distance) {
|
|
3024
|
+
// @ts-ignore
|
|
3025
|
+
return pathLengthFactory(pathInput, distance);
|
|
3026
|
+
}
|
|
3027
|
+
|
|
2781
3028
|
/**
|
|
2782
3029
|
* Creates a new SVGPathCommander instance with the following properties:
|
|
2783
3030
|
* * segments: `pathArray`
|
|
@@ -2797,18 +3044,25 @@ class SVGPathCommander {
|
|
|
2797
3044
|
constructor(pathValue, config) {
|
|
2798
3045
|
const instanceOptions = config || {};
|
|
2799
3046
|
|
|
3047
|
+
const undefPath = typeof pathValue === 'undefined';
|
|
3048
|
+
|
|
3049
|
+
if (undefPath || !pathValue.length) {
|
|
3050
|
+
throw TypeError(`${error}: "pathValue" is ${undefPath ? 'undefined' : 'empty'}`);
|
|
3051
|
+
}
|
|
3052
|
+
|
|
3053
|
+
const segments = parsePathString(pathValue);
|
|
3054
|
+
if (typeof segments === 'string') {
|
|
3055
|
+
throw TypeError(segments);
|
|
3056
|
+
}
|
|
3057
|
+
|
|
2800
3058
|
/**
|
|
2801
|
-
* @type {
|
|
3059
|
+
* @type {SVGPath.pathArray}
|
|
2802
3060
|
*/
|
|
2803
|
-
this.segments =
|
|
2804
|
-
|
|
3061
|
+
this.segments = segments;
|
|
3062
|
+
|
|
2805
3063
|
const {
|
|
2806
|
-
width,
|
|
2807
|
-
|
|
2808
|
-
cx,
|
|
2809
|
-
cy,
|
|
2810
|
-
cz,
|
|
2811
|
-
} = BBox;
|
|
3064
|
+
width, height, cx, cy, cz,
|
|
3065
|
+
} = this.getBBox();
|
|
2812
3066
|
|
|
2813
3067
|
// set instance options.round
|
|
2814
3068
|
let { round, origin } = defaultOptions;
|
|
@@ -2817,7 +3071,7 @@ class SVGPathCommander {
|
|
|
2817
3071
|
if (roundOption === 'auto') {
|
|
2818
3072
|
const pathScale = (`${Math.floor(Math.max(width, height))}`).length;
|
|
2819
3073
|
round = pathScale >= 4 ? 0 : 4 - pathScale;
|
|
2820
|
-
} else if (
|
|
3074
|
+
} else if (Number.isInteger(roundOption) || roundOption === false) {
|
|
2821
3075
|
round = roundOption;
|
|
2822
3076
|
}
|
|
2823
3077
|
|
|
@@ -2827,15 +3081,14 @@ class SVGPathCommander {
|
|
|
2827
3081
|
origin = [
|
|
2828
3082
|
!Number.isNaN(originX) ? originX : cx,
|
|
2829
3083
|
!Number.isNaN(originY) ? originY : cy,
|
|
2830
|
-
originZ
|
|
3084
|
+
!Number.isNaN(originZ) ? originZ : cz,
|
|
2831
3085
|
];
|
|
2832
3086
|
} else {
|
|
2833
3087
|
origin = [cx, cy, cz];
|
|
2834
3088
|
}
|
|
2835
3089
|
|
|
2836
3090
|
/**
|
|
2837
|
-
* @type {number |
|
|
2838
|
-
* @default 4
|
|
3091
|
+
* @type {number | false}
|
|
2839
3092
|
*/
|
|
2840
3093
|
this.round = round;
|
|
2841
3094
|
this.origin = origin;
|
|
@@ -2844,7 +3097,37 @@ class SVGPathCommander {
|
|
|
2844
3097
|
}
|
|
2845
3098
|
|
|
2846
3099
|
/**
|
|
2847
|
-
*
|
|
3100
|
+
* Returns the path bounding box, equivalent to native `path.getBBox()`.
|
|
3101
|
+
* @public
|
|
3102
|
+
* @returns {SVGPath.pathBBox}
|
|
3103
|
+
*/
|
|
3104
|
+
getBBox() {
|
|
3105
|
+
return getPathBBox(this.segments);
|
|
3106
|
+
}
|
|
3107
|
+
|
|
3108
|
+
/**
|
|
3109
|
+
* Returns the total path length, equivalent to native `path.getTotalLength()`.
|
|
3110
|
+
* @public
|
|
3111
|
+
* @returns {number}
|
|
3112
|
+
*/
|
|
3113
|
+
getTotalLength() {
|
|
3114
|
+
return getTotalLength(this.segments);
|
|
3115
|
+
}
|
|
3116
|
+
|
|
3117
|
+
/**
|
|
3118
|
+
* Returns an `{x,y}` point in the path stroke at a given length,
|
|
3119
|
+
* equivalent to the native `path.getPointAtLength()`.
|
|
3120
|
+
*
|
|
3121
|
+
* @public
|
|
3122
|
+
* @param {number} length the length
|
|
3123
|
+
* @returns {{x: number, y:number}} the requested point
|
|
3124
|
+
*/
|
|
3125
|
+
getPointAtLength(length) {
|
|
3126
|
+
return getPointAtLength(this.segments, length);
|
|
3127
|
+
}
|
|
3128
|
+
|
|
3129
|
+
/**
|
|
3130
|
+
* Convert path to absolute values
|
|
2848
3131
|
* @public
|
|
2849
3132
|
*/
|
|
2850
3133
|
toAbsolute() {
|
|
@@ -2934,16 +3217,16 @@ class SVGPathCommander {
|
|
|
2934
3217
|
|
|
2935
3218
|
/**
|
|
2936
3219
|
* Transform path using values from an `Object` defined as `transformObject`.
|
|
2937
|
-
* @see
|
|
3220
|
+
* @see SVGPath.transformObject for a quick refference
|
|
2938
3221
|
*
|
|
2939
|
-
* @param {
|
|
3222
|
+
* @param {SVGPath.transformObject} source a `transformObject`as described above
|
|
2940
3223
|
* @public
|
|
2941
3224
|
*/
|
|
2942
3225
|
transform(source) {
|
|
2943
3226
|
if (!source || typeof source !== 'object' || (typeof source === 'object'
|
|
2944
3227
|
&& !['translate', 'rotate', 'skew', 'scale'].some((x) => x in source))) return this;
|
|
2945
3228
|
|
|
2946
|
-
/** @type {
|
|
3229
|
+
/** @type {SVGPath.transformObject} */
|
|
2947
3230
|
const transform = {};
|
|
2948
3231
|
Object.keys(source).forEach((fn) => {
|
|
2949
3232
|
// @ts-ignore
|
|
@@ -2953,18 +3236,18 @@ class SVGPathCommander {
|
|
|
2953
3236
|
|
|
2954
3237
|
// if origin is not specified
|
|
2955
3238
|
// it's important that we have one
|
|
3239
|
+
const [cx, cy, cz] = this.origin;
|
|
2956
3240
|
const { origin } = transform;
|
|
2957
|
-
|
|
3241
|
+
|
|
3242
|
+
if (Array.isArray(origin) && origin.length >= 2) {
|
|
2958
3243
|
const [originX, originY, originZ] = origin.map(Number);
|
|
2959
|
-
const [cx, cy, cz] = this.origin;
|
|
2960
3244
|
transform.origin = [
|
|
2961
3245
|
!Number.isNaN(originX) ? originX : cx,
|
|
2962
3246
|
!Number.isNaN(originY) ? originY : cy,
|
|
2963
3247
|
originZ || cz,
|
|
2964
3248
|
];
|
|
2965
3249
|
} else {
|
|
2966
|
-
|
|
2967
|
-
transform.origin = { ...this.origin };
|
|
3250
|
+
transform.origin = [cx, cy, cz];
|
|
2968
3251
|
}
|
|
2969
3252
|
|
|
2970
3253
|
this.segments = transformPath(segments, transform);
|
|
@@ -3027,7 +3310,7 @@ function getCubicSegArea(x1, y1, c1x, c1y, c2x, c2y, x2, y2) {
|
|
|
3027
3310
|
*
|
|
3028
3311
|
* @see https://github.com/paperjs/paper.js/blob/develop/src/path/Path.js
|
|
3029
3312
|
*
|
|
3030
|
-
* @param {
|
|
3313
|
+
* @param {SVGPath.pathArray} path the shape `pathArray`
|
|
3031
3314
|
* @returns {number} the length of the cubic-bezier segment
|
|
3032
3315
|
*/
|
|
3033
3316
|
function getPathArea(path) {
|
|
@@ -3054,7 +3337,7 @@ function getPathArea(path) {
|
|
|
3054
3337
|
* This is the `pathToCurve` version which is faster and more efficient for
|
|
3055
3338
|
* paths that are `curveArray`.
|
|
3056
3339
|
*
|
|
3057
|
-
* @param {string |
|
|
3340
|
+
* @param {string | SVGPath.curveArray} path the target `pathArray`
|
|
3058
3341
|
* @returns {number} the `curveArray` total length
|
|
3059
3342
|
*/
|
|
3060
3343
|
function getPathLength(path) {
|
|
@@ -3067,285 +3350,24 @@ function getPathLength(path) {
|
|
|
3067
3350
|
return totalLength;
|
|
3068
3351
|
}
|
|
3069
3352
|
|
|
3070
|
-
/**
|
|
3071
|
-
* Returns the length of a A (arc-to) segment,
|
|
3072
|
-
* or an {x,y} point at a given length.
|
|
3073
|
-
*
|
|
3074
|
-
* @param {number} X1 the starting x position
|
|
3075
|
-
* @param {number} Y1 the starting y position
|
|
3076
|
-
* @param {number} RX x-radius of the arc
|
|
3077
|
-
* @param {number} RY y-radius of the arc
|
|
3078
|
-
* @param {number} angle x-axis-rotation of the arc
|
|
3079
|
-
* @param {number} LAF large-arc-flag of the arc
|
|
3080
|
-
* @param {number} SF sweep-flag of the arc
|
|
3081
|
-
* @param {number} X2 the ending x position
|
|
3082
|
-
* @param {number} Y2 the ending y position
|
|
3083
|
-
* @param {number} distance the point distance
|
|
3084
|
-
* @returns {{x: number, y: number} | number} the segment length or point
|
|
3085
|
-
*/
|
|
3086
|
-
function segmentArcFactory(X1, Y1, RX, RY, angle, LAF, SF, X2, Y2, distance) {
|
|
3087
|
-
const cubicSeg = arcToCubic(X1, Y1, RX, RY, angle, LAF, SF, X2, Y2);
|
|
3088
|
-
const distanceIsNumber = typeof distance === 'number';
|
|
3089
|
-
let [x, y] = [X1, Y1];
|
|
3090
|
-
const lengthMargin = 0.001;
|
|
3091
|
-
let totalLength = 0;
|
|
3092
|
-
let cubicSubseg = [];
|
|
3093
|
-
let argsc = [];
|
|
3094
|
-
let segLen = 0;
|
|
3095
|
-
|
|
3096
|
-
if (distanceIsNumber && distance < lengthMargin) {
|
|
3097
|
-
return { x, y };
|
|
3098
|
-
}
|
|
3099
|
-
|
|
3100
|
-
for (let i = 0, ii = cubicSeg.length; i < ii; i += 6) {
|
|
3101
|
-
cubicSubseg = cubicSeg.slice(i, i + 6);
|
|
3102
|
-
argsc = [x, y, ...cubicSubseg];
|
|
3103
|
-
// @ts-ignore
|
|
3104
|
-
segLen = segmentCubicFactory(...argsc);
|
|
3105
|
-
if (distanceIsNumber && totalLength + segLen >= distance) {
|
|
3106
|
-
// @ts-ignore -- this is a `cubicSegment`
|
|
3107
|
-
return segmentCubicFactory(...argsc, distance - totalLength);
|
|
3108
|
-
}
|
|
3109
|
-
totalLength += segLen;
|
|
3110
|
-
[x, y] = cubicSubseg.slice(-2);
|
|
3111
|
-
}
|
|
3112
|
-
|
|
3113
|
-
if (distanceIsNumber && distance >= totalLength) {
|
|
3114
|
-
return { x: X2, y: Y2 };
|
|
3115
|
-
}
|
|
3116
|
-
|
|
3117
|
-
return totalLength;
|
|
3118
|
-
}
|
|
3119
|
-
|
|
3120
|
-
/**
|
|
3121
|
-
* Returns the {x,y} coordinates of a point at a
|
|
3122
|
-
* given length of a quad-bezier segment.
|
|
3123
|
-
*
|
|
3124
|
-
* @see https://github.com/substack/point-at-length
|
|
3125
|
-
*
|
|
3126
|
-
* @param {number} x1 the starting point X
|
|
3127
|
-
* @param {number} y1 the starting point Y
|
|
3128
|
-
* @param {number} cx the control point X
|
|
3129
|
-
* @param {number} cy the control point Y
|
|
3130
|
-
* @param {number} x2 the ending point X
|
|
3131
|
-
* @param {number} y2 the ending point Y
|
|
3132
|
-
* @param {number} t a [0-1] ratio
|
|
3133
|
-
* @returns {{x: number, y: number}} the requested {x,y} coordinates
|
|
3134
|
-
*/
|
|
3135
|
-
function getPointAtQuadSegmentLength(x1, y1, cx, cy, x2, y2, t) {
|
|
3136
|
-
const t1 = 1 - t;
|
|
3137
|
-
return {
|
|
3138
|
-
x: (t1 ** 2) * x1
|
|
3139
|
-
+ 2 * t1 * t * cx
|
|
3140
|
-
+ (t ** 2) * x2,
|
|
3141
|
-
y: (t1 ** 2) * y1
|
|
3142
|
-
+ 2 * t1 * t * cy
|
|
3143
|
-
+ (t ** 2) * y2,
|
|
3144
|
-
};
|
|
3145
|
-
}
|
|
3146
|
-
|
|
3147
|
-
/**
|
|
3148
|
-
* Returns the Q (quadratic-bezier) segment length,
|
|
3149
|
-
* or an {x,y} point at a given length.
|
|
3150
|
-
*
|
|
3151
|
-
* @param {number} x1 the starting point X
|
|
3152
|
-
* @param {number} y1 the starting point Y
|
|
3153
|
-
* @param {number} qx the control point X
|
|
3154
|
-
* @param {number} qy the control point Y
|
|
3155
|
-
* @param {number} x2 the ending point X
|
|
3156
|
-
* @param {number} y2 the ending point Y
|
|
3157
|
-
* @param {number=} distance the distance to point
|
|
3158
|
-
* @returns {{x: number, y: number} | number} the segment length or point
|
|
3159
|
-
*/
|
|
3160
|
-
function segmentQuadFactory(x1, y1, qx, qy, x2, y2, distance) {
|
|
3161
|
-
const distanceIsNumber = typeof distance === 'number';
|
|
3162
|
-
const lengthMargin = 0.001;
|
|
3163
|
-
let x = x1; let y = y1;
|
|
3164
|
-
let totalLength = 0;
|
|
3165
|
-
let prev = [x1, y1, totalLength];
|
|
3166
|
-
/** @type {[number, number]} */
|
|
3167
|
-
let cur = [x1, y1];
|
|
3168
|
-
let t = 0;
|
|
3169
|
-
|
|
3170
|
-
if (distanceIsNumber && distance < lengthMargin) {
|
|
3171
|
-
return { x, y };
|
|
3172
|
-
}
|
|
3173
|
-
|
|
3174
|
-
const n = 100;
|
|
3175
|
-
for (let j = 0; j <= n; j += 1) {
|
|
3176
|
-
t = j / n;
|
|
3177
|
-
|
|
3178
|
-
({ x, y } = getPointAtQuadSegmentLength(x1, y1, qx, qy, x2, y2, t));
|
|
3179
|
-
totalLength += distanceSquareRoot(cur, [x, y]);
|
|
3180
|
-
cur = [x, y];
|
|
3181
|
-
|
|
3182
|
-
if (distanceIsNumber && totalLength >= distance) {
|
|
3183
|
-
const dv = (totalLength - distance) / (totalLength - prev[2]);
|
|
3184
|
-
|
|
3185
|
-
return {
|
|
3186
|
-
x: cur[0] * (1 - dv) + prev[0] * dv,
|
|
3187
|
-
y: cur[1] * (1 - dv) + prev[1] * dv,
|
|
3188
|
-
};
|
|
3189
|
-
}
|
|
3190
|
-
prev = [x, y, totalLength];
|
|
3191
|
-
}
|
|
3192
|
-
if (distanceIsNumber && distance >= totalLength) {
|
|
3193
|
-
return { x: x2, y: y2 };
|
|
3194
|
-
}
|
|
3195
|
-
return totalLength;
|
|
3196
|
-
}
|
|
3197
|
-
|
|
3198
|
-
/**
|
|
3199
|
-
* Returns a {x,y} point at a given length of a shape or the shape total length.
|
|
3200
|
-
*
|
|
3201
|
-
* @param {string | SVGPathCommander.pathArray} pathInput the `pathArray` to look into
|
|
3202
|
-
* @param {number=} distance the length of the shape to look at
|
|
3203
|
-
* @returns {{x: number, y: number} | number} the total length or point
|
|
3204
|
-
*/
|
|
3205
|
-
function pathLengthFactory(pathInput, distance) {
|
|
3206
|
-
const path = fixPath(normalizePath(pathInput));
|
|
3207
|
-
const distanceIsNumber = typeof distance === 'number';
|
|
3208
|
-
let totalLength = 0;
|
|
3209
|
-
let isM = true;
|
|
3210
|
-
/** @type {number[]} */
|
|
3211
|
-
let data = [];
|
|
3212
|
-
let pathCommand = 'M';
|
|
3213
|
-
let segLen = 0;
|
|
3214
|
-
let x = 0;
|
|
3215
|
-
let y = 0;
|
|
3216
|
-
let mx = 0;
|
|
3217
|
-
let my = 0;
|
|
3218
|
-
let seg;
|
|
3219
|
-
|
|
3220
|
-
for (let i = 0, ll = path.length; i < ll; i += 1) {
|
|
3221
|
-
seg = path[i];
|
|
3222
|
-
[pathCommand] = seg;
|
|
3223
|
-
isM = pathCommand === 'M';
|
|
3224
|
-
// @ts-ignore
|
|
3225
|
-
data = !isM ? [x, y, ...seg.slice(1)] : data;
|
|
3226
|
-
|
|
3227
|
-
// this segment is always ZERO
|
|
3228
|
-
if (isM) {
|
|
3229
|
-
// remember mx, my for Z
|
|
3230
|
-
// @ts-ignore
|
|
3231
|
-
[, mx, my] = seg;
|
|
3232
|
-
if (distanceIsNumber && distance < 0.001) {
|
|
3233
|
-
return { x: mx, y: my };
|
|
3234
|
-
}
|
|
3235
|
-
} else if (pathCommand === 'L') {
|
|
3236
|
-
// @ts-ignore
|
|
3237
|
-
segLen = segmentLineFactory(...data);
|
|
3238
|
-
if (distanceIsNumber && totalLength + segLen >= distance) {
|
|
3239
|
-
// @ts-ignore
|
|
3240
|
-
return segmentLineFactory(...data, distance - totalLength);
|
|
3241
|
-
}
|
|
3242
|
-
totalLength += segLen;
|
|
3243
|
-
} else if (pathCommand === 'A') {
|
|
3244
|
-
// @ts-ignore
|
|
3245
|
-
segLen = segmentArcFactory(...data);
|
|
3246
|
-
if (distanceIsNumber && totalLength + segLen >= distance) {
|
|
3247
|
-
// @ts-ignore
|
|
3248
|
-
return segmentArcFactory(...data, distance - totalLength);
|
|
3249
|
-
}
|
|
3250
|
-
totalLength += segLen;
|
|
3251
|
-
} else if (pathCommand === 'C') {
|
|
3252
|
-
// @ts-ignore
|
|
3253
|
-
segLen = segmentCubicFactory(...data);
|
|
3254
|
-
if (distanceIsNumber && totalLength + segLen >= distance) {
|
|
3255
|
-
// @ts-ignore
|
|
3256
|
-
return segmentCubicFactory(...data, distance - totalLength);
|
|
3257
|
-
}
|
|
3258
|
-
totalLength += segLen;
|
|
3259
|
-
} else if (pathCommand === 'Q') {
|
|
3260
|
-
// @ts-ignore
|
|
3261
|
-
segLen = segmentQuadFactory(...data);
|
|
3262
|
-
if (distanceIsNumber && totalLength + segLen >= distance) {
|
|
3263
|
-
// @ts-ignore
|
|
3264
|
-
return segmentQuadFactory(...data, distance - totalLength);
|
|
3265
|
-
}
|
|
3266
|
-
totalLength += segLen;
|
|
3267
|
-
} else if (pathCommand === 'Z') {
|
|
3268
|
-
data = [x, y, mx, my];
|
|
3269
|
-
// @ts-ignore
|
|
3270
|
-
segLen = segmentLineFactory(...data);
|
|
3271
|
-
if (distanceIsNumber && totalLength + segLen >= distance) {
|
|
3272
|
-
// @ts-ignore
|
|
3273
|
-
return segmentLineFactory(...data, distance - totalLength);
|
|
3274
|
-
}
|
|
3275
|
-
totalLength += segLen;
|
|
3276
|
-
}
|
|
3277
|
-
|
|
3278
|
-
// @ts-ignore -- needed for the below
|
|
3279
|
-
[x, y] = pathCommand !== 'Z' ? seg.slice(-2) : [mx, my];
|
|
3280
|
-
}
|
|
3281
|
-
|
|
3282
|
-
// native `getPointAtLength` behavior when the given distance
|
|
3283
|
-
// is higher than total length
|
|
3284
|
-
if (distanceIsNumber && distance >= totalLength) {
|
|
3285
|
-
return { x, y };
|
|
3286
|
-
}
|
|
3287
|
-
|
|
3288
|
-
return totalLength;
|
|
3289
|
-
}
|
|
3290
|
-
|
|
3291
|
-
/**
|
|
3292
|
-
* Returns the shape total length, or the equivalent to `shape.getTotalLength()`.
|
|
3293
|
-
*
|
|
3294
|
-
* The `normalizePath` version is lighter, faster, more efficient and more accurate
|
|
3295
|
-
* with paths that are not `curveArray`.
|
|
3296
|
-
*
|
|
3297
|
-
* @param {string | SVGPathCommander.pathArray} pathInput the target `pathArray`
|
|
3298
|
-
* @returns {number} the shape total length
|
|
3299
|
-
*/
|
|
3300
|
-
function getTotalLength(pathInput) {
|
|
3301
|
-
// @ts-ignore - it's fine
|
|
3302
|
-
return pathLengthFactory(pathInput);
|
|
3303
|
-
}
|
|
3304
|
-
|
|
3305
3353
|
/**
|
|
3306
3354
|
* Check if a path is drawn clockwise and returns true if so,
|
|
3307
3355
|
* false otherwise.
|
|
3308
3356
|
*
|
|
3309
|
-
* @param {
|
|
3357
|
+
* @param {SVGPath.pathArray} path the path string or `pathArray`
|
|
3310
3358
|
* @returns {boolean} true when clockwise or false if not
|
|
3311
3359
|
*/
|
|
3312
3360
|
function getDrawDirection(path) {
|
|
3313
3361
|
return getPathArea(pathToCurve(path)) >= 0;
|
|
3314
3362
|
}
|
|
3315
3363
|
|
|
3316
|
-
/**
|
|
3317
|
-
* Returns [x,y] coordinates of a point at a given length of a shape.
|
|
3318
|
-
*
|
|
3319
|
-
* @param {string | SVGPathCommander.pathArray} pathInput the `pathArray` to look into
|
|
3320
|
-
* @param {number} distance the length of the shape to look at
|
|
3321
|
-
* @returns {{x: number, y: number}} the requested {x, y} point coordinates
|
|
3322
|
-
*/
|
|
3323
|
-
function getPointAtLength(pathInput, distance) {
|
|
3324
|
-
// @ts-ignore
|
|
3325
|
-
return pathLengthFactory(pathInput, distance);
|
|
3326
|
-
}
|
|
3327
|
-
|
|
3328
|
-
/**
|
|
3329
|
-
* Returns [x,y] coordinates of a point at a given length of a shape.
|
|
3330
|
-
* `pathToCurve` version.
|
|
3331
|
-
*
|
|
3332
|
-
* @deprecated
|
|
3333
|
-
*
|
|
3334
|
-
* @param {string | SVGPathCommander.pathArray} pathInput the `pathArray` to look into
|
|
3335
|
-
* @param {number} distance the length of the shape to look at
|
|
3336
|
-
* @returns {{x: number, y: number}} the requested {x, y} point coordinates
|
|
3337
|
-
*/
|
|
3338
|
-
function getPointAtPathLength(pathInput, distance) {
|
|
3339
|
-
return getPointAtLength(pathInput, distance);
|
|
3340
|
-
}
|
|
3341
|
-
|
|
3342
3364
|
/**
|
|
3343
3365
|
* Returns the segment, its index and length as well as
|
|
3344
3366
|
* the length to that segment at a given length in a path.
|
|
3345
3367
|
*
|
|
3346
|
-
* @param {string |
|
|
3368
|
+
* @param {string | SVGPath.pathArray} pathInput target `pathArray`
|
|
3347
3369
|
* @param {number=} distance the given length
|
|
3348
|
-
* @returns {
|
|
3370
|
+
* @returns {SVGPath.segmentProperties=} the requested properties
|
|
3349
3371
|
*/
|
|
3350
3372
|
function getPropertiesAtLength(pathInput, distance) {
|
|
3351
3373
|
const pathArray = parsePathString(pathInput);
|
|
@@ -3357,7 +3379,7 @@ function getPropertiesAtLength(pathInput, distance) {
|
|
|
3357
3379
|
let index = pathTemp.length - 1;
|
|
3358
3380
|
let lengthAtSegment = 0;
|
|
3359
3381
|
let length = 0;
|
|
3360
|
-
/** @type {
|
|
3382
|
+
/** @type {SVGPath.pathSegment} */
|
|
3361
3383
|
let segment = pathArray[0];
|
|
3362
3384
|
const [x, y] = segment.slice(-2);
|
|
3363
3385
|
const point = { x, y };
|
|
@@ -3405,9 +3427,9 @@ function getPropertiesAtLength(pathInput, distance) {
|
|
|
3405
3427
|
* the distance to the path stroke.
|
|
3406
3428
|
* @see https://bl.ocks.org/mbostock/8027637
|
|
3407
3429
|
*
|
|
3408
|
-
* @param {string |
|
|
3430
|
+
* @param {string | SVGPath.pathArray} pathInput target `pathArray`
|
|
3409
3431
|
* @param {{x: number, y: number}} point the given point
|
|
3410
|
-
* @returns {
|
|
3432
|
+
* @returns {SVGPath.pointProperties} the requested properties
|
|
3411
3433
|
*/
|
|
3412
3434
|
function getPropertiesAtPoint(pathInput, point) {
|
|
3413
3435
|
const path = fixPath(parsePathString(pathInput));
|
|
@@ -3475,7 +3497,7 @@ function getPropertiesAtPoint(pathInput, point) {
|
|
|
3475
3497
|
/**
|
|
3476
3498
|
* Returns the point in path closest to a given point.
|
|
3477
3499
|
*
|
|
3478
|
-
* @param {string |
|
|
3500
|
+
* @param {string | SVGPath.pathArray} pathInput target `pathArray`
|
|
3479
3501
|
* @param {{x: number, y: number}} point the given point
|
|
3480
3502
|
* @returns {{x: number, y: number}} the best match
|
|
3481
3503
|
*/
|
|
@@ -3486,9 +3508,9 @@ function getClosestPoint(pathInput, point) {
|
|
|
3486
3508
|
/**
|
|
3487
3509
|
* Returns the path segment which contains a given point.
|
|
3488
3510
|
*
|
|
3489
|
-
* @param {string |
|
|
3511
|
+
* @param {string | SVGPath.pathArray} path the `pathArray` to look into
|
|
3490
3512
|
* @param {{x: number, y: number}} point the point of the shape to look for
|
|
3491
|
-
* @returns {
|
|
3513
|
+
* @returns {SVGPath.pathSegment?} the requested segment
|
|
3492
3514
|
*/
|
|
3493
3515
|
function getSegmentOfPoint(path, point) {
|
|
3494
3516
|
const props = getPropertiesAtPoint(path, point);
|
|
@@ -3498,9 +3520,9 @@ function getSegmentOfPoint(path, point) {
|
|
|
3498
3520
|
|
|
3499
3521
|
/**
|
|
3500
3522
|
* Returns the segment at a given length.
|
|
3501
|
-
* @param {string |
|
|
3523
|
+
* @param {string | SVGPath.pathArray} pathInput the target `pathArray`
|
|
3502
3524
|
* @param {number} distance the distance in path to look at
|
|
3503
|
-
* @returns {
|
|
3525
|
+
* @returns {SVGPath.pathSegment?} the requested segment
|
|
3504
3526
|
*/
|
|
3505
3527
|
function getSegmentAtLength(pathInput, distance) {
|
|
3506
3528
|
const props = getPropertiesAtLength(pathInput, distance);
|
|
@@ -3511,8 +3533,8 @@ function getSegmentAtLength(pathInput, distance) {
|
|
|
3511
3533
|
/**
|
|
3512
3534
|
* Checks if a given point is in the stroke of a path.
|
|
3513
3535
|
*
|
|
3514
|
-
* @param {string |
|
|
3515
|
-
* @param {{x: number, y: number}} point
|
|
3536
|
+
* @param {string | SVGPath.pathArray} pathInput target path
|
|
3537
|
+
* @param {{x: number, y: number}} point the given `{x,y}` point
|
|
3516
3538
|
* @returns {boolean} the query result
|
|
3517
3539
|
*/
|
|
3518
3540
|
function isPointInStroke(pathInput, point) {
|
|
@@ -3559,8 +3581,8 @@ const shapeParams = {
|
|
|
3559
3581
|
/**
|
|
3560
3582
|
* Returns a new `pathArray` from line attributes.
|
|
3561
3583
|
*
|
|
3562
|
-
* @param {
|
|
3563
|
-
* @returns {
|
|
3584
|
+
* @param {SVGPath.lineAttr} attr shape configuration
|
|
3585
|
+
* @returns {SVGPath.pathArray} a new line `pathArray`
|
|
3564
3586
|
*/
|
|
3565
3587
|
function getLinePath(attr) {
|
|
3566
3588
|
const {
|
|
@@ -3572,11 +3594,11 @@ function getLinePath(attr) {
|
|
|
3572
3594
|
/**
|
|
3573
3595
|
* Returns a new `pathArray` like from polyline/polygon attributes.
|
|
3574
3596
|
*
|
|
3575
|
-
* @param {
|
|
3576
|
-
* @return {
|
|
3597
|
+
* @param {SVGPath.polyAttr} attr shape configuration
|
|
3598
|
+
* @return {SVGPath.pathArray} a new polygon/polyline `pathArray`
|
|
3577
3599
|
*/
|
|
3578
3600
|
function getPolyPath(attr) {
|
|
3579
|
-
/** @type {
|
|
3601
|
+
/** @type {SVGPath.pathArray} */
|
|
3580
3602
|
// @ts-ignore -- it's an empty `pathArray`
|
|
3581
3603
|
const pathArray = [];
|
|
3582
3604
|
const points = attr.points.trim().split(/[\s|,]/).map(Number);
|
|
@@ -3593,8 +3615,8 @@ function getPolyPath(attr) {
|
|
|
3593
3615
|
/**
|
|
3594
3616
|
* Returns a new `pathArray` from circle attributes.
|
|
3595
3617
|
*
|
|
3596
|
-
* @param {
|
|
3597
|
-
* @return {
|
|
3618
|
+
* @param {SVGPath.circleAttr} attr shape configuration
|
|
3619
|
+
* @return {SVGPath.pathArray} a circle `pathArray`
|
|
3598
3620
|
*/
|
|
3599
3621
|
function getCirclePath(attr) {
|
|
3600
3622
|
const {
|
|
@@ -3611,8 +3633,8 @@ function getCirclePath(attr) {
|
|
|
3611
3633
|
/**
|
|
3612
3634
|
* Returns a new `pathArray` from ellipse attributes.
|
|
3613
3635
|
*
|
|
3614
|
-
* @param {
|
|
3615
|
-
* @return {
|
|
3636
|
+
* @param {SVGPath.ellipseAttr} attr shape configuration
|
|
3637
|
+
* @return {SVGPath.pathArray} an ellipse `pathArray`
|
|
3616
3638
|
*/
|
|
3617
3639
|
function getEllipsePath(attr) {
|
|
3618
3640
|
const {
|
|
@@ -3629,8 +3651,8 @@ function getEllipsePath(attr) {
|
|
|
3629
3651
|
/**
|
|
3630
3652
|
* Returns a new `pathArray` like from rect attributes.
|
|
3631
3653
|
*
|
|
3632
|
-
* @param {
|
|
3633
|
-
* @return {
|
|
3654
|
+
* @param {SVGPath.rectAttr} attr object with properties above
|
|
3655
|
+
* @return {SVGPath.pathArray} a new `pathArray` from `<rect>` attributes
|
|
3634
3656
|
*/
|
|
3635
3657
|
function getRectanglePath(attr) {
|
|
3636
3658
|
const x = +attr.x || 0;
|
|
@@ -3676,12 +3698,12 @@ function getRectanglePath(attr) {
|
|
|
3676
3698
|
* is `true`, it will replace the target.
|
|
3677
3699
|
*
|
|
3678
3700
|
* It can also work with an options object,
|
|
3679
|
-
* @see
|
|
3701
|
+
* @see SVGPath.shapeOps
|
|
3680
3702
|
*
|
|
3681
3703
|
* The newly created `<path>` element keeps all non-specific
|
|
3682
3704
|
* attributes like `class`, `fill`, etc.
|
|
3683
3705
|
*
|
|
3684
|
-
* @param {
|
|
3706
|
+
* @param {SVGPath.shapeTypes | SVGPath.shapeOps} element target shape
|
|
3685
3707
|
* @param {boolean=} replace option to replace target
|
|
3686
3708
|
* @return {SVGPathElement | boolean} the newly created `<path>` element
|
|
3687
3709
|
*/
|
|
@@ -3735,11 +3757,11 @@ function shapeToPath(element, replace) {
|
|
|
3735
3757
|
}
|
|
3736
3758
|
|
|
3737
3759
|
/**
|
|
3738
|
-
* Reverses all segments
|
|
3760
|
+
* Reverses all segments of a `pathArray`
|
|
3739
3761
|
* which consists of only C (cubic-bezier) path commands.
|
|
3740
3762
|
*
|
|
3741
|
-
* @param {
|
|
3742
|
-
* @returns {
|
|
3763
|
+
* @param {SVGPath.curveArray} path the source `pathArray`
|
|
3764
|
+
* @returns {SVGPath.curveArray} the reversed `pathArray`
|
|
3743
3765
|
*/
|
|
3744
3766
|
function reverseCurve(path) {
|
|
3745
3767
|
const rotatedCurve = path.slice(1)
|
|
@@ -3776,7 +3798,6 @@ const Util = {
|
|
|
3776
3798
|
getTotalLength,
|
|
3777
3799
|
getPathLength,
|
|
3778
3800
|
getPointAtLength,
|
|
3779
|
-
getPointAtPathLength,
|
|
3780
3801
|
getClosestPoint,
|
|
3781
3802
|
getSegmentOfPoint,
|
|
3782
3803
|
getPropertiesAtPoint,
|
|
@@ -3796,7 +3817,7 @@ const Util = {
|
|
|
3796
3817
|
options: defaultOptions,
|
|
3797
3818
|
};
|
|
3798
3819
|
|
|
3799
|
-
var version = "0.
|
|
3820
|
+
var version = "0.2.0alpha3";
|
|
3800
3821
|
|
|
3801
3822
|
// @ts-ignore
|
|
3802
3823
|
|