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
package/src/interface.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type { PathSegment } from './types';
|
|
2
|
+
|
|
3
|
+
export interface SegmentProperties {
|
|
4
|
+
segment: PathSegment;
|
|
5
|
+
index: number;
|
|
6
|
+
length: number;
|
|
7
|
+
lengthAtSegment: number;
|
|
8
|
+
[key: string]: any;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface PointProperties {
|
|
12
|
+
closest: {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
};
|
|
16
|
+
distance: number;
|
|
17
|
+
segment?: SegmentProperties;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface LineAttr {
|
|
21
|
+
type: 'line';
|
|
22
|
+
x1: number;
|
|
23
|
+
y1: number;
|
|
24
|
+
x2: number;
|
|
25
|
+
y2: number;
|
|
26
|
+
[key: string]: string | number;
|
|
27
|
+
}
|
|
28
|
+
export interface PolyAttr {
|
|
29
|
+
type: 'polygon' | 'polyline';
|
|
30
|
+
points: string;
|
|
31
|
+
[key: string]: string | number;
|
|
32
|
+
}
|
|
33
|
+
export interface CircleAttr {
|
|
34
|
+
type: 'circle';
|
|
35
|
+
cx: number;
|
|
36
|
+
cy: number;
|
|
37
|
+
r: number;
|
|
38
|
+
[key: string]: string | number;
|
|
39
|
+
}
|
|
40
|
+
export interface EllipseAttr {
|
|
41
|
+
type: 'ellipse';
|
|
42
|
+
cx: number;
|
|
43
|
+
cy: number;
|
|
44
|
+
rx: number;
|
|
45
|
+
ry: number;
|
|
46
|
+
[key: string]: string | number;
|
|
47
|
+
}
|
|
48
|
+
export interface RectAttr {
|
|
49
|
+
type: 'rect';
|
|
50
|
+
width: number;
|
|
51
|
+
height: number;
|
|
52
|
+
x: number;
|
|
53
|
+
y: number;
|
|
54
|
+
rx: number;
|
|
55
|
+
ry: number;
|
|
56
|
+
[key: string]: string | number;
|
|
57
|
+
}
|
|
58
|
+
export interface GlyphAttr {
|
|
59
|
+
type: 'glyph';
|
|
60
|
+
d: string;
|
|
61
|
+
[key: string]: string | number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface ShapeParams {
|
|
65
|
+
line: ['x1', 'y1', 'x2', 'y2'];
|
|
66
|
+
circle: ['cx', 'cy', 'r'];
|
|
67
|
+
ellipse: ['cx', 'cy', 'rx', 'ry'];
|
|
68
|
+
rect: ['width', 'height', 'x', 'y', 'rx', 'ry'];
|
|
69
|
+
polygon: ['points'];
|
|
70
|
+
polyline: ['points'];
|
|
71
|
+
glyph: ['d'];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface PathBBox {
|
|
75
|
+
width: number;
|
|
76
|
+
height: number;
|
|
77
|
+
x: number;
|
|
78
|
+
y: number;
|
|
79
|
+
x2: number;
|
|
80
|
+
y2: number;
|
|
81
|
+
cx: number;
|
|
82
|
+
cy: number;
|
|
83
|
+
cz: number;
|
|
84
|
+
}
|
|
85
|
+
export interface SegmentLimits {
|
|
86
|
+
min: { x: number; y: number };
|
|
87
|
+
max: { x: number; y: number };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface ParserParams {
|
|
91
|
+
x1: number;
|
|
92
|
+
y1: number;
|
|
93
|
+
x2: number;
|
|
94
|
+
y2: number;
|
|
95
|
+
x: number;
|
|
96
|
+
y: number;
|
|
97
|
+
qx: number | null;
|
|
98
|
+
qy: number | null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface LengthFactory {
|
|
102
|
+
length: number;
|
|
103
|
+
point: { x: number; y: number };
|
|
104
|
+
min: { x: number; y: number };
|
|
105
|
+
max: { x: number; y: number };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface Options {
|
|
109
|
+
round: 'auto' | 'off' | number;
|
|
110
|
+
origin: number[];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface PathTransform {
|
|
114
|
+
s: PathSegment;
|
|
115
|
+
c: string;
|
|
116
|
+
x: number;
|
|
117
|
+
y: number;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface TransformObject {
|
|
121
|
+
translate: number | number[];
|
|
122
|
+
rotate: number | number[];
|
|
123
|
+
scale: number | number[];
|
|
124
|
+
skew: number | number[];
|
|
125
|
+
origin: number[];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export type TransformProps = keyof TransformObject;
|
|
129
|
+
export type TransformEntries = [TransformProps, TransformObject[TransformProps]][];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the square root of the distance
|
|
3
|
+
* between two given points.
|
|
4
|
+
*
|
|
5
|
+
* @param a the first point coordinates
|
|
6
|
+
* @param b the second point coordinates
|
|
7
|
+
* @returns the distance value
|
|
8
|
+
*/
|
|
9
|
+
const distanceSquareRoot = (a: [number, number], b: [number, number]): number => {
|
|
10
|
+
return Math.sqrt((a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]));
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default distanceSquareRoot;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the coordinates of a specified distance
|
|
3
|
+
* ratio between two points.
|
|
4
|
+
*
|
|
5
|
+
* @param a the first point coordinates
|
|
6
|
+
* @param b the second point coordinates
|
|
7
|
+
* @param t the ratio
|
|
8
|
+
* @returns the midpoint coordinates
|
|
9
|
+
*/
|
|
10
|
+
const midPoint = (a: [number, number], b: [number, number], t: number): [number, number] => {
|
|
11
|
+
const [ax, ay] = a;
|
|
12
|
+
const [bx, by] = b;
|
|
13
|
+
return [ax + (bx - ax) * t, ay + (by - ay) * t];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default midPoint;
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Returns the area of a polygon.
|
|
6
6
|
*
|
|
7
|
-
* @param
|
|
8
|
-
* @returns
|
|
7
|
+
* @param polygon an array of coordinates
|
|
8
|
+
* @returns the polygon area
|
|
9
9
|
*/
|
|
10
|
-
|
|
10
|
+
const polygonArea = (polygon: [number, number][]): number => {
|
|
11
11
|
const n = polygon.length;
|
|
12
12
|
let i = -1;
|
|
13
13
|
let a;
|
|
@@ -22,4 +22,6 @@ export default function polygonArea(polygon) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
return area / 2;
|
|
25
|
-
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default polygonArea;
|
|
@@ -6,14 +6,16 @@ import distanceSquareRoot from './distanceSquareRoot';
|
|
|
6
6
|
*
|
|
7
7
|
* Returns the perimeter of a polygon.
|
|
8
8
|
*
|
|
9
|
-
* @param
|
|
9
|
+
* @param polygon an array of coordinates
|
|
10
10
|
* @returns {number} the polygon length
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
const polygonLength = (polygon: [number, number][]): number => {
|
|
13
13
|
return polygon.reduce((length, point, i) => {
|
|
14
14
|
if (i) {
|
|
15
15
|
return length + distanceSquareRoot(polygon[i - 1], point);
|
|
16
16
|
}
|
|
17
17
|
return 0;
|
|
18
18
|
}, 0);
|
|
19
|
-
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default polygonLength;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns an {x,y} vector rotated by a given
|
|
3
|
+
* angle in radian.
|
|
4
|
+
*
|
|
5
|
+
* @param x the initial vector x
|
|
6
|
+
* @param y the initial vector y
|
|
7
|
+
* @param rad the radian vector angle
|
|
8
|
+
* @returns the rotated vector
|
|
9
|
+
*/
|
|
10
|
+
const rotateVector = (x: number, y: number, rad: number): { x: number; y: number } => {
|
|
11
|
+
const X = x * Math.cos(rad) - y * Math.sin(rad);
|
|
12
|
+
const Y = x * Math.sin(rad) + y * Math.cos(rad);
|
|
13
|
+
return { x: X, y: Y };
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default rotateVector;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import paramsCount from './paramsCount';
|
|
2
|
+
import PathParser from './pathParser';
|
|
3
|
+
import type { PathCommand, PathSegment, RelativeCommand } from '../types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Breaks the parsing of a pathString once a segment is finalized.
|
|
7
|
+
*
|
|
8
|
+
* @param path the `PathParser` instance
|
|
9
|
+
*/
|
|
10
|
+
const finalizeSegment = (path: PathParser) => {
|
|
11
|
+
let pathCommand = path.pathValue[path.segmentStart] as PathCommand;
|
|
12
|
+
let LK = pathCommand.toLowerCase() as RelativeCommand;
|
|
13
|
+
const { data } = path;
|
|
14
|
+
|
|
15
|
+
while (data.length >= paramsCount[LK]) {
|
|
16
|
+
// overloaded `moveTo`
|
|
17
|
+
// https://github.com/rveciana/svg-path-properties/blob/master/src/parse.ts
|
|
18
|
+
if (LK === 'm' && data.length > 2) {
|
|
19
|
+
path.segments.push([pathCommand, ...(data.splice(0, 2) as number[])] as PathSegment);
|
|
20
|
+
LK = 'l';
|
|
21
|
+
pathCommand = pathCommand === 'm' ? 'l' : 'L';
|
|
22
|
+
} else {
|
|
23
|
+
path.segments.push([pathCommand, ...(data.splice(0, paramsCount[LK]) as number[])] as PathSegment);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!paramsCount[LK]) {
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
export default finalizeSegment;
|
|
File without changes
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Checks if the character is an A (arc-to) path command.
|
|
3
3
|
*
|
|
4
|
-
* @param
|
|
5
|
-
* @returns
|
|
4
|
+
* @param code the character to check
|
|
5
|
+
* @returns check result
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
const isArcCommand = (code: number): code is 0x61 => {
|
|
8
8
|
// eslint-disable-next-line no-bitwise -- Impossible to satisfy
|
|
9
9
|
return (code | 0x20) === 0x61;
|
|
10
|
-
}
|
|
10
|
+
};
|
|
11
|
+
export default isArcCommand;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DigitNumber } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Checks if a character is a digit.
|
|
5
|
+
*
|
|
6
|
+
* @param code the character to check
|
|
7
|
+
* @returns check result
|
|
8
|
+
*/
|
|
9
|
+
const isDigit = (code: number): code is DigitNumber => {
|
|
10
|
+
return code >= 48 && code <= 57; // 0..9
|
|
11
|
+
};
|
|
12
|
+
export default isDigit;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import isDigit from './isDigit';
|
|
2
|
+
import type { DigitNumber } from '../types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Checks if the character is or belongs to a number.
|
|
6
|
+
* [0-9]|+|-|.
|
|
7
|
+
*
|
|
8
|
+
* @param code the character to check
|
|
9
|
+
* @returns check result
|
|
10
|
+
*/
|
|
11
|
+
const isDigitStart = (code: number): code is DigitNumber | 0x2b | 0x2d | 0x2e => {
|
|
12
|
+
return isDigit(code) /* 0..9 */ || code === 0x2b /* + */ || code === 0x2d /* - */ || code === 0x2e; /* . */
|
|
13
|
+
};
|
|
14
|
+
export default isDigitStart;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { PathCommandNumber } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Checks if the character is a path command.
|
|
5
|
+
*
|
|
6
|
+
* @param code the character to check
|
|
7
|
+
* @returns check result
|
|
8
|
+
*/
|
|
9
|
+
const isPathCommand = (code: number): code is PathCommandNumber => {
|
|
10
|
+
// eslint-disable-next-line no-bitwise -- Impossible to satisfy
|
|
11
|
+
switch (code | 0x20) {
|
|
12
|
+
case 0x6d /* m */:
|
|
13
|
+
case 0x7a /* z */:
|
|
14
|
+
case 0x6c /* l */:
|
|
15
|
+
case 0x68 /* h */:
|
|
16
|
+
case 0x76 /* v */:
|
|
17
|
+
case 0x63 /* c */:
|
|
18
|
+
case 0x73 /* s */:
|
|
19
|
+
case 0x71 /* q */:
|
|
20
|
+
case 0x74 /* t */:
|
|
21
|
+
case 0x61 /* a */:
|
|
22
|
+
// case 0x72/* r */:
|
|
23
|
+
return true;
|
|
24
|
+
default:
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
export default isPathCommand;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SpaceNumber } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Checks if the character is a space.
|
|
5
|
+
*
|
|
6
|
+
* @param ch the character to check
|
|
7
|
+
* @returns check result
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const isSpace = (ch: number): ch is SpaceNumber => {
|
|
11
|
+
const allSpaces = [
|
|
12
|
+
// Special spaces
|
|
13
|
+
0x1680, 0x180e, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200a, 0x202f,
|
|
14
|
+
0x205f, 0x3000, 0xfeff,
|
|
15
|
+
// Line terminators
|
|
16
|
+
0x0a, 0x0d, 0x2028, 0x2029,
|
|
17
|
+
// White spaces
|
|
18
|
+
0x20, 0x09, 0x0b, 0x0c, 0xa0,
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
return allSpaces.includes(ch);
|
|
22
|
+
};
|
|
23
|
+
export default isSpace;
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import scanSegment from './scanSegment';
|
|
2
2
|
import skipSpaces from './skipSpaces';
|
|
3
|
-
import clonePath from '../process/clonePath';
|
|
4
3
|
import PathParser from './pathParser';
|
|
5
4
|
import isPathArray from '../util/isPathArray';
|
|
5
|
+
import type { PathArray } from '../types';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Parses a path string value and returns an array
|
|
9
9
|
* of segments we like to call `pathArray`.
|
|
10
10
|
*
|
|
11
|
-
* @param
|
|
12
|
-
* @returns
|
|
11
|
+
* @param pathInput the string to be parsed
|
|
12
|
+
* @returns the resulted `pathArray` or error string
|
|
13
13
|
*/
|
|
14
|
-
|
|
14
|
+
const parsePathString = (pathInput: string | PathArray): PathArray => {
|
|
15
15
|
if (isPathArray(pathInput)) {
|
|
16
|
-
return
|
|
16
|
+
return [...pathInput];
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
const path = new PathParser(pathInput);
|
|
@@ -24,5 +24,11 @@ export default function parsePathString(pathInput) {
|
|
|
24
24
|
scanSegment(path);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
if (path.err && path.err.length) {
|
|
28
|
+
throw TypeError(path.err);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return path.segments as PathArray;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default parsePathString;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { PathArray, PathSegment } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The `PathParser` is used by the `parsePathString` static method
|
|
5
|
+
* to generate a `pathArray`.
|
|
6
|
+
*
|
|
7
|
+
* @param pathString
|
|
8
|
+
*/
|
|
9
|
+
export default class PathParser {
|
|
10
|
+
declare segments: PathArray | PathSegment[];
|
|
11
|
+
declare pathValue: string;
|
|
12
|
+
declare max: number;
|
|
13
|
+
declare index: number;
|
|
14
|
+
declare param: number;
|
|
15
|
+
declare segmentStart: number;
|
|
16
|
+
declare data: any[];
|
|
17
|
+
declare err: string;
|
|
18
|
+
|
|
19
|
+
constructor(pathString: string) {
|
|
20
|
+
this.segments = [];
|
|
21
|
+
this.pathValue = pathString;
|
|
22
|
+
this.max = pathString.length;
|
|
23
|
+
this.index = 0;
|
|
24
|
+
this.param = 0.0;
|
|
25
|
+
this.segmentStart = 0;
|
|
26
|
+
this.data = [];
|
|
27
|
+
this.err = '';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -1,26 +1,29 @@
|
|
|
1
1
|
import error from './error';
|
|
2
|
+
import type PathParser from './pathParser';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Validates an A (arc-to) specific path command value.
|
|
5
6
|
* Usually a `large-arc-flag` or `sweep-flag`.
|
|
6
7
|
*
|
|
7
|
-
* @param
|
|
8
|
+
* @param path the `PathParser` instance
|
|
8
9
|
*/
|
|
9
|
-
|
|
10
|
+
const scanFlag = (path: PathParser) => {
|
|
10
11
|
const { index, pathValue } = path;
|
|
11
12
|
const code = pathValue.charCodeAt(index);
|
|
12
13
|
|
|
13
|
-
if (code === 0x30/* 0 */) {
|
|
14
|
+
if (code === 0x30 /* 0 */) {
|
|
14
15
|
path.param = 0;
|
|
15
16
|
path.index += 1;
|
|
16
17
|
return;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
if (code === 0x31/* 1 */) {
|
|
20
|
+
if (code === 0x31 /* 1 */) {
|
|
20
21
|
path.param = 1;
|
|
21
22
|
path.index += 1;
|
|
22
23
|
return;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
path.err = `${error}: invalid Arc flag "${pathValue[index]}", expecting 0 or 1 at index ${index}`;
|
|
26
|
-
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default scanFlag;
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import isDigit from './isDigit';
|
|
2
2
|
import invalidPathValue from './invalidPathValue';
|
|
3
3
|
import error from './error';
|
|
4
|
+
import type PathParser from './pathParser';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Validates every character of the path string,
|
|
7
8
|
* every path command, negative numbers or floating point numbers.
|
|
8
9
|
*
|
|
9
|
-
* @param
|
|
10
|
+
* @param path the `PathParser` instance
|
|
10
11
|
*/
|
|
11
|
-
|
|
12
|
+
const scanParam = (path: PathParser) => {
|
|
12
13
|
const { max, pathValue, index: start } = path;
|
|
13
14
|
let index = start;
|
|
14
15
|
let zeroFirst = false;
|
|
@@ -18,13 +19,12 @@ export default function scanParam(path) {
|
|
|
18
19
|
let ch;
|
|
19
20
|
|
|
20
21
|
if (index >= max) {
|
|
21
|
-
// path.err = 'SvgPath: missed param (at pos ' + index + ')';
|
|
22
22
|
path.err = `${error}: ${invalidPathValue} at index ${index}, "pathValue" is missing param`;
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
25
|
ch = pathValue.charCodeAt(index);
|
|
26
26
|
|
|
27
|
-
if (ch ===
|
|
27
|
+
if (ch === 0x2b /* + */ || ch === 0x2d /* - */) {
|
|
28
28
|
index += 1;
|
|
29
29
|
// ch = (index < max) ? pathValue.charCodeAt(index) : 0;
|
|
30
30
|
ch = pathValue.charCodeAt(index);
|
|
@@ -32,14 +32,14 @@ export default function scanParam(path) {
|
|
|
32
32
|
|
|
33
33
|
// This logic is shamelessly borrowed from Esprima
|
|
34
34
|
// https://github.com/ariya/esprimas
|
|
35
|
-
if (!isDigit(ch) && ch !==
|
|
35
|
+
if (!isDigit(ch) && ch !== 0x2e /* . */) {
|
|
36
36
|
// path.err = 'SvgPath: param should start with 0..9 or `.` (at pos ' + index + ')';
|
|
37
37
|
path.err = `${error}: ${invalidPathValue} at index ${index}, "${pathValue[index]}" is not a number`;
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
if (ch !==
|
|
42
|
-
zeroFirst =
|
|
41
|
+
if (ch !== 0x2e /* . */) {
|
|
42
|
+
zeroFirst = ch === 0x30 /* 0 */;
|
|
43
43
|
index += 1;
|
|
44
44
|
|
|
45
45
|
ch = pathValue.charCodeAt(index);
|
|
@@ -62,7 +62,7 @@ export default function scanParam(path) {
|
|
|
62
62
|
ch = pathValue.charCodeAt(index);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
if (ch ===
|
|
65
|
+
if (ch === 0x2e /* . */) {
|
|
66
66
|
hasDot = true;
|
|
67
67
|
index += 1;
|
|
68
68
|
while (isDigit(pathValue.charCodeAt(index))) {
|
|
@@ -73,7 +73,7 @@ export default function scanParam(path) {
|
|
|
73
73
|
ch = pathValue.charCodeAt(index);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
if (ch === 0x65/* e */ || ch === 0x45/* E */) {
|
|
76
|
+
if (ch === 0x65 /* e */ || ch === 0x45 /* E */) {
|
|
77
77
|
if (hasDot && !hasCeiling && !hasDecimal) {
|
|
78
78
|
path.err = `${error}: ${invalidPathValue} at index ${index}, "${pathValue[index]}" invalid float exponent`;
|
|
79
79
|
return;
|
|
@@ -83,7 +83,7 @@ export default function scanParam(path) {
|
|
|
83
83
|
|
|
84
84
|
ch = pathValue.charCodeAt(index);
|
|
85
85
|
|
|
86
|
-
if (ch ===
|
|
86
|
+
if (ch === 0x2b /* + */ || ch === 0x2d /* - */) {
|
|
87
87
|
index += 1;
|
|
88
88
|
}
|
|
89
89
|
if (index < max && isDigit(pathValue.charCodeAt(index))) {
|
|
@@ -98,4 +98,5 @@ export default function scanParam(path) {
|
|
|
98
98
|
|
|
99
99
|
path.index = index;
|
|
100
100
|
path.param = +path.pathValue.slice(start, index);
|
|
101
|
-
}
|
|
101
|
+
};
|
|
102
|
+
export default scanParam;
|
|
@@ -9,16 +9,19 @@ import isArcCommand from './isArcCommand';
|
|
|
9
9
|
import invalidPathValue from './invalidPathValue';
|
|
10
10
|
import error from './error';
|
|
11
11
|
|
|
12
|
+
import type PathParser from './pathParser';
|
|
13
|
+
import { RelativeCommand } from '../types';
|
|
14
|
+
|
|
12
15
|
/**
|
|
13
16
|
* Scans every character in the path string to determine
|
|
14
17
|
* where a segment starts and where it ends.
|
|
15
18
|
*
|
|
16
|
-
* @param
|
|
19
|
+
* @param path the `PathParser` instance
|
|
17
20
|
*/
|
|
18
|
-
|
|
21
|
+
const scanSegment = (path: PathParser) => {
|
|
19
22
|
const { max, pathValue, index } = path;
|
|
20
23
|
const cmdCode = pathValue.charCodeAt(index);
|
|
21
|
-
const reqParams = paramCounts[pathValue[index].toLowerCase()];
|
|
24
|
+
const reqParams = paramCounts[pathValue[index].toLowerCase() as RelativeCommand];
|
|
22
25
|
|
|
23
26
|
path.segmentStart = index;
|
|
24
27
|
|
|
@@ -51,7 +54,7 @@ export default function scanSegment(path) {
|
|
|
51
54
|
skipSpaces(path);
|
|
52
55
|
|
|
53
56
|
// after ',' param is mandatory
|
|
54
|
-
if (path.index < max && pathValue.charCodeAt(path.index) ===
|
|
57
|
+
if (path.index < max && pathValue.charCodeAt(path.index) === 0x2c /* , */) {
|
|
55
58
|
path.index += 1;
|
|
56
59
|
skipSpaces(path);
|
|
57
60
|
}
|
|
@@ -68,4 +71,5 @@ export default function scanSegment(path) {
|
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
finalizeSegment(path);
|
|
71
|
-
}
|
|
74
|
+
};
|
|
75
|
+
export default scanSegment;
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import isSpace from './isSpace';
|
|
2
|
+
import type PathParser from './pathParser';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Points the parser to the next character in the
|
|
5
6
|
* path string every time it encounters any kind of
|
|
6
7
|
* space character.
|
|
7
8
|
*
|
|
8
|
-
* @param
|
|
9
|
+
* @param path the `PathParser` instance
|
|
9
10
|
*/
|
|
10
|
-
|
|
11
|
+
const skipSpaces = (path: PathParser) => {
|
|
11
12
|
const { pathValue, max } = path;
|
|
12
13
|
while (path.index < max && isSpace(pathValue.charCodeAt(path.index))) {
|
|
13
14
|
path.index += 1;
|
|
14
15
|
}
|
|
15
|
-
}
|
|
16
|
+
};
|
|
17
|
+
export default skipSpaces;
|