svg-path-commander 1.0.5 → 2.0.0-alpha2
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 +26 -4
- package/dist/svg-path-commander.cjs +2 -0
- package/dist/svg-path-commander.cjs.map +1 -0
- package/dist/svg-path-commander.d.ts +454 -0
- package/dist/svg-path-commander.js +2 -3762
- package/dist/svg-path-commander.js.map +1 -0
- package/dist/svg-path-commander.mjs +1194 -0
- package/dist/svg-path-commander.mjs.map +1 -0
- package/package.json +39 -42
- package/src/convert/pathToAbsolute.ts +101 -0
- package/src/convert/{pathToCurve.js → pathToCurve.ts} +12 -14
- package/src/convert/pathToRelative.ts +92 -0
- package/src/convert/pathToString.ts +17 -0
- package/src/{svg-path-commander.js → index.ts} +143 -66
- package/src/interface.ts +129 -0
- package/src/math/distanceSquareRoot.ts +13 -0
- package/src/math/midPoint.ts +16 -0
- package/src/math/{polygonArea.js → polygonArea.ts} +6 -4
- package/src/math/{polygonLength.js → polygonLength.ts} +5 -3
- package/src/math/rotateVector.ts +16 -0
- package/src/options/options.ts +9 -0
- package/src/parser/error.ts +2 -0
- package/src/parser/finalizeSegment.ts +31 -0
- package/src/parser/{invalidPathValue.js → invalidPathValue.ts} +0 -0
- package/src/parser/{isArcCommand.js → isArcCommand.ts} +5 -4
- package/src/parser/isDigit.ts +12 -0
- package/src/parser/isDigitStart.ts +14 -0
- package/src/parser/isPathCommand.ts +28 -0
- package/src/parser/isSpace.ts +23 -0
- package/src/parser/paramsCount.ts +16 -0
- package/src/parser/paramsParser.ts +14 -0
- package/src/parser/{parsePathString.js → parsePathString.ts} +13 -7
- package/src/parser/pathParser.ts +29 -0
- package/src/parser/{scanFlag.js → scanFlag.ts} +8 -5
- package/src/parser/{scanParam.js → scanParam.ts} +12 -11
- package/src/parser/{scanSegment.js → scanSegment.ts} +9 -5
- package/src/parser/{skipSpaces.js → skipSpaces.ts} +5 -3
- package/src/process/{arcToCubic.js → arcToCubic.ts} +42 -28
- package/src/process/fixArc.ts +23 -0
- package/src/process/getSVGMatrix.ts +70 -0
- package/src/process/lineToCubic.ts +17 -0
- package/src/process/{normalizePath.js → normalizePath.ts} +10 -10
- package/src/process/normalizeSegment.ts +47 -0
- package/src/process/{optimizePath.js → optimizePath.ts} +18 -21
- package/src/process/{projection2d.js → projection2d.ts} +16 -15
- package/src/process/quadToCubic.ts +31 -0
- package/src/process/reverseCurve.ts +21 -0
- package/src/process/reversePath.ts +101 -0
- package/src/process/roundPath.ts +29 -0
- package/src/process/segmentToCubic.ts +46 -0
- package/src/process/shortenSegment.ts +79 -0
- package/src/process/splitCubic.ts +28 -0
- package/src/process/{splitPath.js → splitPath.ts} +9 -8
- package/src/process/{transformPath.js → transformPath.ts} +55 -48
- package/src/types.ts +193 -0
- package/src/util/getClosestPoint.ts +15 -0
- package/src/util/{getDrawDirection.js → getDrawDirection.ts} +7 -4
- package/src/util/getPathArea.ts +70 -0
- package/src/util/{getPathBBox.js → getPathBBox.ts} +16 -5
- package/src/util/getPointAtLength.ts +14 -0
- package/src/util/{getPropertiesAtLength.js → getPropertiesAtLength.ts} +28 -19
- package/src/util/{getPropertiesAtPoint.js → getPropertiesAtPoint.ts} +16 -12
- package/src/util/getSegmentAtLength.ts +15 -0
- package/src/util/getSegmentOfPoint.ts +18 -0
- package/src/util/{getTotalLength.js → getTotalLength.ts} +6 -4
- package/src/util/isAbsoluteArray.ts +18 -0
- package/src/util/{isCurveArray.js → isCurveArray.ts} +6 -4
- package/src/util/{isNormalizedArray.js → isNormalizedArray.ts} +4 -2
- package/src/util/isPathArray.ts +19 -0
- package/src/util/isPointInStroke.ts +15 -0
- package/src/util/isRelativeArray.ts +18 -0
- package/src/util/{isValidPath.js → isValidPath.ts} +5 -4
- package/src/util/{pathLengthFactory.js → pathLengthFactory.ts} +38 -31
- package/src/util/{segmentArcFactory.js → segmentArcFactory.ts} +71 -55
- package/src/util/segmentCubicFactory.ts +114 -0
- package/src/util/{segmentLineFactory.js → segmentLineFactory.ts} +10 -8
- package/src/util/{segmentQuadFactory.js → segmentQuadFactory.ts} +47 -32
- package/src/util/shapeToPath.ts +214 -0
- package/dist/svg-path-commander.es5.js +0 -3875
- package/dist/svg-path-commander.es5.min.js +0 -2
- package/dist/svg-path-commander.esm.js +0 -3754
- package/dist/svg-path-commander.esm.min.js +0 -2
- package/dist/svg-path-commander.min.js +0 -2
- package/src/convert/pathToAbsolute.js +0 -86
- package/src/convert/pathToRelative.js +0 -84
- package/src/convert/pathToString.js +0 -14
- package/src/index.js +0 -10
- package/src/math/distanceSquareRoot.js +0 -14
- package/src/math/epsilon.js +0 -8
- package/src/math/midPoint.js +0 -13
- package/src/math/rotateVector.js +0 -14
- package/src/options/options.js +0 -10
- package/src/parser/error.js +0 -2
- package/src/parser/finalizeSegment.js +0 -28
- package/src/parser/isDigit.js +0 -9
- package/src/parser/isDigitStart.js +0 -13
- package/src/parser/isPathCommand.js +0 -25
- package/src/parser/isSpace.js +0 -16
- package/src/parser/paramsCount.js +0 -9
- package/src/parser/paramsParser.js +0 -8
- package/src/parser/pathParser.js +0 -24
- package/src/process/clonePath.js +0 -9
- package/src/process/fixArc.js +0 -21
- package/src/process/fixPath.js +0 -31
- package/src/process/getSVGMatrix.js +0 -61
- package/src/process/lineToCubic.js +0 -30
- package/src/process/normalizeSegment.js +0 -45
- package/src/process/quadToCubic.js +0 -22
- package/src/process/reverseCurve.js +0 -18
- package/src/process/reversePath.js +0 -89
- package/src/process/roundPath.js +0 -26
- package/src/process/segmentToCubic.js +0 -46
- package/src/process/shortenSegment.js +0 -58
- package/src/process/splitCubic.js +0 -26
- package/src/util/getClosestPoint.js +0 -12
- package/src/util/getPathArea.js +0 -47
- package/src/util/getPointAtLength.js +0 -12
- package/src/util/getSegmentAtLength.js +0 -11
- package/src/util/getSegmentOfPoint.js +0 -12
- package/src/util/isAbsoluteArray.js +0 -14
- package/src/util/isPathArray.js +0 -14
- package/src/util/isPointInStroke.js +0 -13
- package/src/util/isRelativeArray.js +0 -14
- package/src/util/segmentCubicFactory.js +0 -97
- package/src/util/shapeToPath.js +0 -204
- package/src/util/util.js +0 -82
- package/src/version.js +0 -8
- package/types/index.d.ts +0 -120
- package/types/more/modules.ts +0 -82
- package/types/more/svg.d.ts +0 -211
- package/types/svg-path-commander.d.ts +0 -1089
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import parsePathString from '../parser/parsePathString';
|
|
2
|
+
import isAbsoluteArray from '../util/isAbsoluteArray';
|
|
3
|
+
import type {
|
|
4
|
+
PathArray,
|
|
5
|
+
AbsoluteArray,
|
|
6
|
+
AbsoluteCommand,
|
|
7
|
+
AbsoluteSegment,
|
|
8
|
+
VSegment,
|
|
9
|
+
HSegment,
|
|
10
|
+
QSegment,
|
|
11
|
+
TSegment,
|
|
12
|
+
ASegment,
|
|
13
|
+
SSegment,
|
|
14
|
+
CSegment,
|
|
15
|
+
MSegment,
|
|
16
|
+
} from '../types';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Parses a path string value or object and returns an array
|
|
20
|
+
* of segments, all converted to absolute values.
|
|
21
|
+
*
|
|
22
|
+
* @param pathInput the path string | object
|
|
23
|
+
* @returns the resulted `pathArray` with absolute values
|
|
24
|
+
*/
|
|
25
|
+
const pathToAbsolute = (pathInput: string | PathArray): AbsoluteArray => {
|
|
26
|
+
/* istanbul ignore else */
|
|
27
|
+
if (isAbsoluteArray(pathInput)) {
|
|
28
|
+
return [...pathInput];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const path = parsePathString(pathInput);
|
|
32
|
+
let x = 0;
|
|
33
|
+
let y = 0;
|
|
34
|
+
let mx = 0;
|
|
35
|
+
let my = 0;
|
|
36
|
+
|
|
37
|
+
// the `absoluteSegment[]` is for sure an `absolutePath`
|
|
38
|
+
return path.map(segment => {
|
|
39
|
+
const values = segment.slice(1).map(Number);
|
|
40
|
+
const [pathCommand] = segment;
|
|
41
|
+
const absCommand = pathCommand.toUpperCase() as AbsoluteCommand;
|
|
42
|
+
|
|
43
|
+
if (pathCommand === 'M') {
|
|
44
|
+
[x, y] = values;
|
|
45
|
+
mx = x;
|
|
46
|
+
my = y;
|
|
47
|
+
return ['M', x, y] as MSegment;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let absoluteSegment = [] as unknown as AbsoluteSegment;
|
|
51
|
+
|
|
52
|
+
if (pathCommand !== absCommand) {
|
|
53
|
+
if (absCommand === 'A') {
|
|
54
|
+
absoluteSegment = [
|
|
55
|
+
absCommand,
|
|
56
|
+
values[0],
|
|
57
|
+
values[1],
|
|
58
|
+
values[2],
|
|
59
|
+
values[3],
|
|
60
|
+
values[4],
|
|
61
|
+
values[5] + x,
|
|
62
|
+
values[6] + y,
|
|
63
|
+
] as ASegment;
|
|
64
|
+
} else if (absCommand === 'V') {
|
|
65
|
+
absoluteSegment = [absCommand, values[0] + y] as VSegment;
|
|
66
|
+
} else if (absCommand === 'H') {
|
|
67
|
+
absoluteSegment = [absCommand, values[0] + x] as HSegment;
|
|
68
|
+
} else {
|
|
69
|
+
// use brakets for `eslint: no-case-declaration`
|
|
70
|
+
// https://stackoverflow.com/a/50753272/803358
|
|
71
|
+
const absValues = values.map((n, j) => n + (j % 2 ? y : x));
|
|
72
|
+
// for n, l, c, s, q, t
|
|
73
|
+
absoluteSegment = [absCommand, ...absValues] as QSegment | TSegment | SSegment | CSegment;
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
absoluteSegment = [absCommand, ...values] as typeof segment;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// const segLength = absoluteSegment.length;
|
|
80
|
+
if (absCommand === 'Z') {
|
|
81
|
+
x = mx;
|
|
82
|
+
y = my;
|
|
83
|
+
} else if (absCommand === 'H') {
|
|
84
|
+
[, x] = absoluteSegment as HSegment;
|
|
85
|
+
} else if (absCommand === 'V') {
|
|
86
|
+
[, y] = absoluteSegment as VSegment;
|
|
87
|
+
} else {
|
|
88
|
+
// x = absoluteSegment[segLength - 2];
|
|
89
|
+
// y = absoluteSegment[segLength - 1];
|
|
90
|
+
[x, y] = absoluteSegment.slice(-2) as [number, number];
|
|
91
|
+
|
|
92
|
+
if (absCommand === 'M') {
|
|
93
|
+
mx = x;
|
|
94
|
+
my = y;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return absoluteSegment;
|
|
99
|
+
}) as AbsoluteArray;
|
|
100
|
+
};
|
|
101
|
+
export default pathToAbsolute;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import fixArc from '../process/fixArc';
|
|
2
|
-
// import fixPath from '../process/fixPath';
|
|
3
2
|
import isCurveArray from '../util/isCurveArray';
|
|
4
|
-
import clonePath from '../process/clonePath';
|
|
5
3
|
import normalizePath from '../process/normalizePath';
|
|
6
4
|
import segmentToCubic from '../process/segmentToCubic';
|
|
7
5
|
import paramsParser from '../parser/paramsParser';
|
|
6
|
+
import { CurveArray, PathArray, PathCommand } from '../types';
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* Parses a path string value or 'pathArray' and returns a new one
|
|
@@ -13,26 +12,24 @@ import paramsParser from '../parser/paramsParser';
|
|
|
13
12
|
* In addition, un-necessary `Z` segment is removed if previous segment
|
|
14
13
|
* extends to the `M` segment.
|
|
15
14
|
*
|
|
16
|
-
* @param
|
|
17
|
-
* @returns
|
|
15
|
+
* @param pathInput the string to be parsed or 'pathArray'
|
|
16
|
+
* @returns the resulted `pathArray` converted to cubic-bezier
|
|
18
17
|
*/
|
|
19
|
-
|
|
18
|
+
const pathToCurve = (pathInput: string | PathArray): CurveArray => {
|
|
20
19
|
/* istanbul ignore else */
|
|
21
20
|
if (isCurveArray(pathInput)) {
|
|
22
|
-
|
|
23
|
-
return clonePath(pathInput);
|
|
21
|
+
return [...pathInput];
|
|
24
22
|
}
|
|
25
23
|
|
|
26
|
-
// const path = fixPath(normalizePath(pathInput));
|
|
27
24
|
const path = normalizePath(pathInput);
|
|
28
25
|
const params = { ...paramsParser };
|
|
29
|
-
const allPathCommands = [];
|
|
26
|
+
const allPathCommands = [] as PathCommand[];
|
|
30
27
|
let pathCommand = ''; // ts-lint
|
|
31
28
|
let ii = path.length;
|
|
32
29
|
|
|
33
30
|
for (let i = 0; i < ii; i += 1) {
|
|
34
31
|
[pathCommand] = path[i];
|
|
35
|
-
allPathCommands[i] = pathCommand;
|
|
32
|
+
allPathCommands[i] = pathCommand as PathCommand;
|
|
36
33
|
|
|
37
34
|
path[i] = segmentToCubic(path[i], params);
|
|
38
35
|
|
|
@@ -43,9 +40,10 @@ export default function pathToCurve(pathInput) {
|
|
|
43
40
|
const seglen = segment.length;
|
|
44
41
|
params.x1 = +segment[seglen - 2];
|
|
45
42
|
params.y1 = +segment[seglen - 1];
|
|
46
|
-
params.x2 = +
|
|
47
|
-
params.y2 = +
|
|
43
|
+
params.x2 = +segment[seglen - 4] || params.x1;
|
|
44
|
+
params.y2 = +segment[seglen - 3] || params.y1;
|
|
48
45
|
}
|
|
49
46
|
|
|
50
|
-
return path;
|
|
51
|
-
}
|
|
47
|
+
return path as CurveArray;
|
|
48
|
+
};
|
|
49
|
+
export default pathToCurve;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
aSegment,
|
|
3
|
+
hSegment,
|
|
4
|
+
PathArray,
|
|
5
|
+
RelativeArray,
|
|
6
|
+
RelativeCommand,
|
|
7
|
+
RelativeSegment,
|
|
8
|
+
vSegment,
|
|
9
|
+
} from '../types';
|
|
10
|
+
import parsePathString from '../parser/parsePathString';
|
|
11
|
+
import isRelativeArray from '../util/isRelativeArray';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Parses a path string value or object and returns an array
|
|
15
|
+
* of segments, all converted to relative values.
|
|
16
|
+
*
|
|
17
|
+
* @param pathInput the path string | object
|
|
18
|
+
* @returns the resulted `pathArray` with relative values
|
|
19
|
+
*/
|
|
20
|
+
const pathToRelative = (pathInput: string | PathArray): RelativeArray => {
|
|
21
|
+
/* istanbul ignore else */
|
|
22
|
+
if (isRelativeArray(pathInput)) {
|
|
23
|
+
return [...pathInput];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const path = parsePathString(pathInput);
|
|
27
|
+
let x = 0;
|
|
28
|
+
let y = 0;
|
|
29
|
+
let mx = 0;
|
|
30
|
+
let my = 0;
|
|
31
|
+
|
|
32
|
+
return path.map(segment => {
|
|
33
|
+
const values = segment.slice(1).map(Number);
|
|
34
|
+
const [pathCommand] = segment;
|
|
35
|
+
const relativeCommand = pathCommand.toLowerCase() as RelativeCommand;
|
|
36
|
+
|
|
37
|
+
if (pathCommand === 'M') {
|
|
38
|
+
[x, y] = values;
|
|
39
|
+
mx = x;
|
|
40
|
+
my = y;
|
|
41
|
+
return ['M', x, y];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let relativeSegment = [];
|
|
45
|
+
|
|
46
|
+
if (pathCommand !== relativeCommand) {
|
|
47
|
+
if (relativeCommand === 'a') {
|
|
48
|
+
relativeSegment = [
|
|
49
|
+
relativeCommand,
|
|
50
|
+
values[0],
|
|
51
|
+
values[1],
|
|
52
|
+
values[2],
|
|
53
|
+
values[3],
|
|
54
|
+
values[4],
|
|
55
|
+
values[5] - x,
|
|
56
|
+
values[6] - y,
|
|
57
|
+
] as aSegment;
|
|
58
|
+
} else if (relativeCommand === 'v') {
|
|
59
|
+
relativeSegment = [relativeCommand, values[0] - y] as vSegment;
|
|
60
|
+
} else if (relativeCommand === 'h') {
|
|
61
|
+
relativeSegment = [relativeCommand, values[0] - x] as hSegment;
|
|
62
|
+
} else {
|
|
63
|
+
// use brakets for `eslint: no-case-declaration`
|
|
64
|
+
// https://stackoverflow.com/a/50753272/803358
|
|
65
|
+
const relValues = values.map((n, j) => n - (j % 2 ? y : x));
|
|
66
|
+
relativeSegment = [relativeCommand, ...relValues] as RelativeSegment;
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
if (pathCommand === 'm') {
|
|
70
|
+
mx = values[0] + x;
|
|
71
|
+
my = values[1] + y;
|
|
72
|
+
}
|
|
73
|
+
relativeSegment = [relativeCommand, ...values] as RelativeSegment;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const segLength = relativeSegment.length;
|
|
77
|
+
if (relativeCommand === 'z') {
|
|
78
|
+
x = mx;
|
|
79
|
+
y = my;
|
|
80
|
+
} else if (relativeCommand === 'h') {
|
|
81
|
+
x += relativeSegment[1] as number;
|
|
82
|
+
} else if (relativeCommand === 'v') {
|
|
83
|
+
y += relativeSegment[1] as number;
|
|
84
|
+
} else {
|
|
85
|
+
x += relativeSegment[segLength - 2] as number;
|
|
86
|
+
y += relativeSegment[segLength - 1] as number;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return relativeSegment as typeof segment;
|
|
90
|
+
}) as RelativeArray;
|
|
91
|
+
};
|
|
92
|
+
export default pathToRelative;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PathArray } from '../types';
|
|
2
|
+
import roundPath from '../process/roundPath';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns a valid `d` attribute string value created
|
|
6
|
+
* by rounding values and concatenating the `pathArray` segments.
|
|
7
|
+
*
|
|
8
|
+
* @param path the `pathArray` object
|
|
9
|
+
* @param round amount of decimals to round values to
|
|
10
|
+
* @returns the concatenated path string
|
|
11
|
+
*/
|
|
12
|
+
const pathToString = (path: PathArray, round?: number | 'off'): string => {
|
|
13
|
+
return roundPath(path, round)
|
|
14
|
+
.map(x => x[0] + x.slice(1).join(' '))
|
|
15
|
+
.join('');
|
|
16
|
+
};
|
|
17
|
+
export default pathToString;
|
|
@@ -1,24 +1,49 @@
|
|
|
1
|
+
import { PathArray, TransformObjectValues } from './types';
|
|
2
|
+
import { Options, PathBBox, TransformEntries, TransformObject } from './interface';
|
|
1
3
|
import defaultOptions from './options/options';
|
|
2
4
|
|
|
3
|
-
import
|
|
4
|
-
import pathToRelative from './convert/pathToRelative';
|
|
5
|
-
import pathToCurve from './convert/pathToCurve';
|
|
6
|
-
import pathToString from './convert/pathToString';
|
|
7
|
-
|
|
5
|
+
import error from './parser/error';
|
|
8
6
|
import parsePathString from './parser/parsePathString';
|
|
9
|
-
import reversePath from './process/reversePath';
|
|
10
7
|
|
|
11
|
-
import
|
|
8
|
+
import polygonArea from './math/polygonArea';
|
|
9
|
+
import polygonLength from './math/polygonLength';
|
|
10
|
+
|
|
11
|
+
import CSSMatrix from '@thednp/dommatrix';
|
|
12
|
+
import getPathBBox from './util/getPathBBox';
|
|
13
|
+
import getPathArea from './util/getPathArea';
|
|
14
|
+
import getTotalLength from './util/getTotalLength';
|
|
15
|
+
import getDrawDirection from './util/getDrawDirection';
|
|
16
|
+
import getPointAtLength from './util/getPointAtLength';
|
|
17
|
+
import pathLengthFactory from './util/pathLengthFactory';
|
|
18
|
+
|
|
19
|
+
import getPropertiesAtLength from './util/getPropertiesAtLength';
|
|
20
|
+
import getPropertiesAtPoint from './util/getPropertiesAtPoint';
|
|
21
|
+
import getClosestPoint from './util/getClosestPoint';
|
|
22
|
+
import getSegmentOfPoint from './util/getSegmentOfPoint';
|
|
23
|
+
import getSegmentAtLength from './util/getSegmentAtLength';
|
|
24
|
+
import isPointInStroke from './util/isPointInStroke';
|
|
25
|
+
|
|
26
|
+
import isValidPath from './util/isValidPath';
|
|
27
|
+
import isPathArray from './util/isPathArray';
|
|
28
|
+
import isAbsoluteArray from './util/isAbsoluteArray';
|
|
29
|
+
import isRelativeArray from './util/isRelativeArray';
|
|
30
|
+
import isCurveArray from './util/isCurveArray';
|
|
31
|
+
import isNormalizedArray from './util/isNormalizedArray';
|
|
32
|
+
import shapeToPath from './util/shapeToPath';
|
|
33
|
+
|
|
34
|
+
import roundPath from './process/roundPath';
|
|
12
35
|
import splitPath from './process/splitPath';
|
|
13
36
|
import optimizePath from './process/optimizePath';
|
|
37
|
+
import reverseCurve from './process/reverseCurve';
|
|
38
|
+
import reversePath from './process/reversePath';
|
|
14
39
|
import normalizePath from './process/normalizePath';
|
|
15
40
|
import transformPath from './process/transformPath';
|
|
41
|
+
import splitCubic from './process/splitCubic';
|
|
16
42
|
|
|
17
|
-
import
|
|
18
|
-
|
|
19
|
-
import
|
|
20
|
-
import
|
|
21
|
-
import getPointAtLength from './util/getPointAtLength';
|
|
43
|
+
import pathToAbsolute from './convert/pathToAbsolute';
|
|
44
|
+
import pathToRelative from './convert/pathToRelative';
|
|
45
|
+
import pathToCurve from './convert/pathToCurve';
|
|
46
|
+
import pathToString from './convert/pathToString';
|
|
22
47
|
|
|
23
48
|
/**
|
|
24
49
|
* Creates a new SVGPathCommander instance with the following properties:
|
|
@@ -31,14 +56,54 @@ import getPointAtLength from './util/getPointAtLength';
|
|
|
31
56
|
* @returns {SVGPathCommander} a new SVGPathCommander instance
|
|
32
57
|
*/
|
|
33
58
|
class SVGPathCommander {
|
|
59
|
+
// bring main utilities to front
|
|
60
|
+
public static CSSMatrix = CSSMatrix;
|
|
61
|
+
public static getPathBBox = getPathBBox;
|
|
62
|
+
public static getPathArea = getPathArea;
|
|
63
|
+
public static getTotalLength = getTotalLength;
|
|
64
|
+
public static getDrawDirection = getDrawDirection;
|
|
65
|
+
public static getPointAtLength = getPointAtLength;
|
|
66
|
+
public static pathLengthFactory = pathLengthFactory;
|
|
67
|
+
public static getPropertiesAtLength = getPropertiesAtLength;
|
|
68
|
+
public static getPropertiesAtPoint = getPropertiesAtPoint;
|
|
69
|
+
public static polygonLength = polygonLength;
|
|
70
|
+
public static polygonArea = polygonArea;
|
|
71
|
+
public static getClosestPoint = getClosestPoint;
|
|
72
|
+
public static getSegmentOfPoint = getSegmentOfPoint;
|
|
73
|
+
public static getSegmentAtLength = getSegmentAtLength;
|
|
74
|
+
public static isPointInStroke = isPointInStroke;
|
|
75
|
+
public static isValidPath = isValidPath;
|
|
76
|
+
public static isPathArray = isPathArray;
|
|
77
|
+
public static isAbsoluteArray = isAbsoluteArray;
|
|
78
|
+
public static isRelativeArray = isRelativeArray;
|
|
79
|
+
public static isCurveArray = isCurveArray;
|
|
80
|
+
public static isNormalizedArray = isNormalizedArray;
|
|
81
|
+
public static shapeToPath = shapeToPath;
|
|
82
|
+
public static parsePathString = parsePathString;
|
|
83
|
+
public static roundPath = roundPath;
|
|
84
|
+
public static splitPath = splitPath;
|
|
85
|
+
public static splitCubic = splitCubic;
|
|
86
|
+
public static optimizePath = optimizePath;
|
|
87
|
+
public static reverseCurve = reverseCurve;
|
|
88
|
+
public static reversePath = reversePath;
|
|
89
|
+
public static normalizePath = normalizePath;
|
|
90
|
+
public static transformPath = transformPath;
|
|
91
|
+
public static pathToAbsolute = pathToAbsolute;
|
|
92
|
+
public static pathToRelative = pathToRelative;
|
|
93
|
+
public static pathToCurve = pathToCurve;
|
|
94
|
+
public static pathToString = pathToString;
|
|
95
|
+
// declare class properties
|
|
96
|
+
declare segments: PathArray;
|
|
97
|
+
declare round: number | 'off';
|
|
98
|
+
declare origin: [number, number, number];
|
|
99
|
+
|
|
34
100
|
/**
|
|
35
101
|
* @constructor
|
|
36
102
|
* @param {string} pathValue the path string
|
|
37
103
|
* @param {any} config instance options
|
|
38
104
|
*/
|
|
39
|
-
constructor(pathValue, config) {
|
|
105
|
+
constructor(pathValue: string, config?: Partial<Options>) {
|
|
40
106
|
const instanceOptions = config || {};
|
|
41
|
-
|
|
42
107
|
const undefPath = typeof pathValue === 'undefined';
|
|
43
108
|
|
|
44
109
|
if (undefPath || !pathValue.length) {
|
|
@@ -46,35 +111,30 @@ class SVGPathCommander {
|
|
|
46
111
|
}
|
|
47
112
|
|
|
48
113
|
const segments = parsePathString(pathValue);
|
|
49
|
-
if (typeof segments === 'string') {
|
|
50
|
-
|
|
51
|
-
}
|
|
114
|
+
// if (typeof segments === 'string') {
|
|
115
|
+
// throw TypeError(segments);
|
|
116
|
+
// }
|
|
52
117
|
|
|
53
|
-
/**
|
|
54
|
-
* @type {SVGPath.pathArray}
|
|
55
|
-
*/
|
|
56
118
|
this.segments = segments;
|
|
57
119
|
|
|
58
|
-
const {
|
|
59
|
-
width, height, cx, cy, cz,
|
|
60
|
-
} = this.getBBox();
|
|
120
|
+
const { width, height, cx, cy, cz } = this.getBBox();
|
|
61
121
|
|
|
62
122
|
// set instance options.round
|
|
63
123
|
const { round: roundOption, origin: originOption } = instanceOptions;
|
|
64
|
-
let round;
|
|
124
|
+
let round: number | 'off';
|
|
65
125
|
|
|
66
126
|
if (roundOption === 'auto') {
|
|
67
|
-
const pathScale =
|
|
127
|
+
const pathScale = `${Math.floor(Math.max(width, height))}`.length;
|
|
68
128
|
round = pathScale >= 4 ? 0 : 4 - pathScale;
|
|
69
129
|
} else if (Number.isInteger(roundOption) || roundOption === 'off') {
|
|
70
|
-
round = roundOption;
|
|
130
|
+
round = roundOption as number | 'off';
|
|
71
131
|
} else {
|
|
72
|
-
|
|
132
|
+
round = defaultOptions.round as number;
|
|
73
133
|
}
|
|
74
134
|
|
|
75
135
|
// set instance options.origin
|
|
76
136
|
// the SVGPathCommander class will always override the default origin
|
|
77
|
-
let origin;
|
|
137
|
+
let origin: [number, number, number];
|
|
78
138
|
if (Array.isArray(originOption) && originOption.length >= 2) {
|
|
79
139
|
const [originX, originY, originZ] = originOption.map(Number);
|
|
80
140
|
origin = [
|
|
@@ -86,9 +146,7 @@ class SVGPathCommander {
|
|
|
86
146
|
origin = [cx, cy, cz];
|
|
87
147
|
}
|
|
88
148
|
|
|
89
|
-
/** @type {number | 'off'} */
|
|
90
149
|
this.round = round;
|
|
91
|
-
/** @type {[number, number, number=]} */
|
|
92
150
|
this.origin = origin;
|
|
93
151
|
|
|
94
152
|
return this;
|
|
@@ -96,17 +154,19 @@ class SVGPathCommander {
|
|
|
96
154
|
|
|
97
155
|
/**
|
|
98
156
|
* Returns the path bounding box, equivalent to native `path.getBBox()`.
|
|
157
|
+
*
|
|
99
158
|
* @public
|
|
100
|
-
* @returns
|
|
159
|
+
* @returns the pathBBox
|
|
101
160
|
*/
|
|
102
|
-
getBBox() {
|
|
161
|
+
getBBox(): PathBBox {
|
|
103
162
|
return getPathBBox(this.segments);
|
|
104
163
|
}
|
|
105
164
|
|
|
106
165
|
/**
|
|
107
166
|
* Returns the total path length, equivalent to native `path.getTotalLength()`.
|
|
167
|
+
*
|
|
108
168
|
* @public
|
|
109
|
-
* @returns
|
|
169
|
+
* @returns the path total length
|
|
110
170
|
*/
|
|
111
171
|
getTotalLength() {
|
|
112
172
|
return getTotalLength(this.segments);
|
|
@@ -117,15 +177,16 @@ class SVGPathCommander {
|
|
|
117
177
|
* equivalent to the native `path.getPointAtLength()`.
|
|
118
178
|
*
|
|
119
179
|
* @public
|
|
120
|
-
* @param
|
|
121
|
-
* @returns
|
|
180
|
+
* @param length the length
|
|
181
|
+
* @returns the requested point
|
|
122
182
|
*/
|
|
123
|
-
getPointAtLength(length) {
|
|
183
|
+
getPointAtLength(length: number): { x: number; y: number } {
|
|
124
184
|
return getPointAtLength(this.segments, length);
|
|
125
185
|
}
|
|
126
186
|
|
|
127
187
|
/**
|
|
128
188
|
* Convert path to absolute values
|
|
189
|
+
*
|
|
129
190
|
* @public
|
|
130
191
|
*/
|
|
131
192
|
toAbsolute() {
|
|
@@ -136,6 +197,7 @@ class SVGPathCommander {
|
|
|
136
197
|
|
|
137
198
|
/**
|
|
138
199
|
* Convert path to relative values
|
|
200
|
+
*
|
|
139
201
|
* @public
|
|
140
202
|
*/
|
|
141
203
|
toRelative() {
|
|
@@ -158,22 +220,26 @@ class SVGPathCommander {
|
|
|
158
220
|
|
|
159
221
|
/**
|
|
160
222
|
* Reverse the order of the segments and their values.
|
|
161
|
-
*
|
|
223
|
+
*
|
|
224
|
+
* @param onlySubpath option to reverse all sub-paths except first
|
|
162
225
|
* @public
|
|
163
226
|
*/
|
|
164
|
-
reverse(onlySubpath) {
|
|
227
|
+
reverse(onlySubpath?: boolean) {
|
|
165
228
|
this.toAbsolute();
|
|
166
229
|
|
|
167
230
|
const { segments } = this;
|
|
168
231
|
const split = splitPath(segments);
|
|
169
|
-
const subPath = split.length > 1 ? split :
|
|
170
|
-
|
|
171
|
-
const absoluteMultiPath = subPath
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
232
|
+
const subPath = split.length > 1 ? split : false;
|
|
233
|
+
|
|
234
|
+
const absoluteMultiPath = subPath
|
|
235
|
+
? [...subPath].map((x, i) => {
|
|
236
|
+
if (onlySubpath) {
|
|
237
|
+
// return i ? reversePath(x) : parsePathString(x);
|
|
238
|
+
return i ? reversePath(x) : [...x];
|
|
239
|
+
}
|
|
240
|
+
return reversePath(x);
|
|
241
|
+
})
|
|
242
|
+
: [...segments];
|
|
177
243
|
|
|
178
244
|
let path = [];
|
|
179
245
|
if (subPath) {
|
|
@@ -182,7 +248,7 @@ class SVGPathCommander {
|
|
|
182
248
|
path = onlySubpath ? segments : reversePath(segments);
|
|
183
249
|
}
|
|
184
250
|
|
|
185
|
-
this.segments =
|
|
251
|
+
this.segments = [...path] as PathArray;
|
|
186
252
|
return this;
|
|
187
253
|
}
|
|
188
254
|
|
|
@@ -190,6 +256,7 @@ class SVGPathCommander {
|
|
|
190
256
|
* Normalize path in 2 steps:
|
|
191
257
|
* * convert `pathArray`(s) to absolute values
|
|
192
258
|
* * convert shorthand notation to standard notation
|
|
259
|
+
*
|
|
193
260
|
* @public
|
|
194
261
|
*/
|
|
195
262
|
normalize() {
|
|
@@ -203,6 +270,7 @@ class SVGPathCommander {
|
|
|
203
270
|
* * convert segments to absolute and/or relative values
|
|
204
271
|
* * select segments with shortest resulted string
|
|
205
272
|
* * round values to the specified `decimals` option value
|
|
273
|
+
*
|
|
206
274
|
* @public
|
|
207
275
|
*/
|
|
208
276
|
optimize() {
|
|
@@ -214,34 +282,40 @@ class SVGPathCommander {
|
|
|
214
282
|
|
|
215
283
|
/**
|
|
216
284
|
* Transform path using values from an `Object` defined as `transformObject`.
|
|
217
|
-
* @see SVGPath.transformObject for a quick refference
|
|
218
285
|
*
|
|
219
|
-
* @
|
|
286
|
+
* @see TransformObject for a quick refference
|
|
287
|
+
*
|
|
288
|
+
* @param source a `transformObject`as described above
|
|
220
289
|
* @public
|
|
221
290
|
*/
|
|
222
|
-
transform(source) {
|
|
223
|
-
if (
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
291
|
+
transform(source?: Partial<TransformObject>) {
|
|
292
|
+
if (
|
|
293
|
+
!source ||
|
|
294
|
+
typeof source !== 'object' ||
|
|
295
|
+
(typeof source === 'object' && !['translate', 'rotate', 'skew', 'scale'].some(x => x in source))
|
|
296
|
+
)
|
|
297
|
+
return this;
|
|
298
|
+
|
|
299
|
+
const {
|
|
300
|
+
segments,
|
|
301
|
+
origin: [cx, cy, cz],
|
|
302
|
+
} = this;
|
|
303
|
+
const transform = {} as TransformObjectValues;
|
|
304
|
+
for (const [k, v] of Object.entries(source) as TransformEntries) {
|
|
305
|
+
if (k === 'skew' && Array.isArray(v)) {
|
|
306
|
+
transform[k] = v.map(Number) as [number, number];
|
|
307
|
+
} else if ((k === 'rotate' || k === 'translate' || k === 'origin' || k === 'scale') && Array.isArray(v)) {
|
|
308
|
+
transform[k] = v.map(Number) as [number, number, number];
|
|
309
|
+
} else if (k !== 'origin' && typeof Number(v) === 'number') transform[k] = Number(v);
|
|
310
|
+
}
|
|
232
311
|
|
|
233
312
|
// if origin is not specified
|
|
234
313
|
// it's important that we have one
|
|
235
|
-
const [cx, cy, cz] = this.origin;
|
|
236
314
|
const { origin } = transform;
|
|
237
315
|
|
|
238
316
|
if (Array.isArray(origin) && origin.length >= 2) {
|
|
239
317
|
const [originX, originY, originZ] = origin.map(Number);
|
|
240
|
-
transform.origin = [
|
|
241
|
-
!Number.isNaN(originX) ? originX : cx,
|
|
242
|
-
!Number.isNaN(originY) ? originY : cy,
|
|
243
|
-
originZ || cz,
|
|
244
|
-
];
|
|
318
|
+
transform.origin = [!Number.isNaN(originX) ? originX : cx, !Number.isNaN(originY) ? originY : cy, originZ || cz];
|
|
245
319
|
} else {
|
|
246
320
|
transform.origin = [cx, cy, cz];
|
|
247
321
|
}
|
|
@@ -252,6 +326,7 @@ class SVGPathCommander {
|
|
|
252
326
|
|
|
253
327
|
/**
|
|
254
328
|
* Rotate path 180deg vertically
|
|
329
|
+
*
|
|
255
330
|
* @public
|
|
256
331
|
*/
|
|
257
332
|
flipX() {
|
|
@@ -261,6 +336,7 @@ class SVGPathCommander {
|
|
|
261
336
|
|
|
262
337
|
/**
|
|
263
338
|
* Rotate path 180deg horizontally
|
|
339
|
+
*
|
|
264
340
|
* @public
|
|
265
341
|
*/
|
|
266
342
|
flipY() {
|
|
@@ -271,8 +347,9 @@ class SVGPathCommander {
|
|
|
271
347
|
/**
|
|
272
348
|
* Export the current path to be used
|
|
273
349
|
* for the `d` (description) attribute.
|
|
350
|
+
*
|
|
274
351
|
* @public
|
|
275
|
-
* @return
|
|
352
|
+
* @return the path string
|
|
276
353
|
*/
|
|
277
354
|
toString() {
|
|
278
355
|
return pathToString(this.segments, this.round);
|