svg-path-commander 2.1.2 → 2.1.5

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.
Files changed (97) hide show
  1. package/README.md +4 -4
  2. package/dist/svg-path-commander.cjs +1 -1
  3. package/dist/svg-path-commander.cjs.map +1 -1
  4. package/dist/svg-path-commander.d.ts +755 -849
  5. package/dist/svg-path-commander.js +1 -1
  6. package/dist/svg-path-commander.js.map +1 -1
  7. package/dist/svg-path-commander.mjs +947 -611
  8. package/dist/svg-path-commander.mjs.map +1 -1
  9. package/package.json +14 -22
  10. package/.eslintrc.cjs +0 -225
  11. package/.prettierrc.json +0 -15
  12. package/dts.config.ts +0 -15
  13. package/src/convert/pathToAbsolute.ts +0 -18
  14. package/src/convert/pathToCurve.ts +0 -43
  15. package/src/convert/pathToRelative.ts +0 -18
  16. package/src/convert/pathToString.ts +0 -50
  17. package/src/index.ts +0 -454
  18. package/src/interface.ts +0 -130
  19. package/src/math/arcTools.ts +0 -396
  20. package/src/math/bezier.ts +0 -261
  21. package/src/math/cubicTools.ts +0 -124
  22. package/src/math/distanceSquareRoot.ts +0 -15
  23. package/src/math/lineTools.ts +0 -69
  24. package/src/math/midPoint.ts +0 -18
  25. package/src/math/polygonTools.ts +0 -48
  26. package/src/math/quadTools.ts +0 -100
  27. package/src/math/rotateVector.ts +0 -17
  28. package/src/math/roundTo.ts +0 -7
  29. package/src/options/options.ts +0 -9
  30. package/src/parser/error.ts +0 -2
  31. package/src/parser/finalizeSegment.ts +0 -35
  32. package/src/parser/invalidPathValue.ts +0 -2
  33. package/src/parser/isArcCommand.ts +0 -11
  34. package/src/parser/isDigit.ts +0 -12
  35. package/src/parser/isDigitStart.ts +0 -14
  36. package/src/parser/isMoveCommand.ts +0 -17
  37. package/src/parser/isPathCommand.ts +0 -28
  38. package/src/parser/isSpace.ts +0 -23
  39. package/src/parser/paramsCount.ts +0 -16
  40. package/src/parser/paramsParser.ts +0 -14
  41. package/src/parser/parsePathString.ts +0 -33
  42. package/src/parser/pathParser.ts +0 -29
  43. package/src/parser/scanFlag.ts +0 -29
  44. package/src/parser/scanParam.ts +0 -102
  45. package/src/parser/scanSegment.ts +0 -84
  46. package/src/parser/skipSpaces.ts +0 -17
  47. package/src/process/absolutizeSegment.ts +0 -63
  48. package/src/process/arcToCubic.ts +0 -128
  49. package/src/process/getSVGMatrix.ts +0 -70
  50. package/src/process/iterate.ts +0 -58
  51. package/src/process/lineToCubic.ts +0 -17
  52. package/src/process/normalizePath.ts +0 -33
  53. package/src/process/normalizeSegment.ts +0 -84
  54. package/src/process/optimizePath.ts +0 -63
  55. package/src/process/projection2d.ts +0 -52
  56. package/src/process/quadToCubic.ts +0 -31
  57. package/src/process/relativizeSegment.ts +0 -59
  58. package/src/process/reverseCurve.ts +0 -24
  59. package/src/process/reversePath.ts +0 -114
  60. package/src/process/roundPath.ts +0 -33
  61. package/src/process/roundSegment.ts +0 -9
  62. package/src/process/segmentToCubic.ts +0 -48
  63. package/src/process/shortenSegment.ts +0 -71
  64. package/src/process/splitCubic.ts +0 -29
  65. package/src/process/splitPath.ts +0 -63
  66. package/src/process/transformPath.ts +0 -110
  67. package/src/types.ts +0 -228
  68. package/src/util/distanceEpsilon.ts +0 -3
  69. package/src/util/getClosestPoint.ts +0 -15
  70. package/src/util/getDrawDirection.ts +0 -16
  71. package/src/util/getPathArea.ts +0 -70
  72. package/src/util/getPathBBox.ts +0 -98
  73. package/src/util/getPointAtLength.ts +0 -110
  74. package/src/util/getPropertiesAtLength.ts +0 -67
  75. package/src/util/getPropertiesAtPoint.ts +0 -84
  76. package/src/util/getSegmentAtLength.ts +0 -15
  77. package/src/util/getSegmentOfPoint.ts +0 -18
  78. package/src/util/getTotalLength.ts +0 -68
  79. package/src/util/isAbsoluteArray.ts +0 -18
  80. package/src/util/isCurveArray.ts +0 -15
  81. package/src/util/isNormalizedArray.ts +0 -16
  82. package/src/util/isPathArray.ts +0 -24
  83. package/src/util/isPointInStroke.ts +0 -16
  84. package/src/util/isRelativeArray.ts +0 -18
  85. package/src/util/isValidPath.ts +0 -27
  86. package/src/util/shapeParams.ts +0 -16
  87. package/src/util/shapeToPath.ts +0 -86
  88. package/src/util/shapeToPathArray.ts +0 -183
  89. package/test/class.test.ts +0 -504
  90. package/test/fixtures/getMarkup.ts +0 -17
  91. package/test/fixtures/shapeObjects.ts +0 -11
  92. package/test/fixtures/shapes.js +0 -104
  93. package/test/fixtures/simpleShapes.js +0 -75
  94. package/test/static.test.ts +0 -330
  95. package/vite.config.mts +0 -41
  96. package/vitest.config-ui.mts +0 -26
  97. package/vitest.config.mts +0 -26
@@ -1,29 +0,0 @@
1
- import error from './error';
2
- import type PathParser from './pathParser';
3
-
4
- /**
5
- * Validates an A (arc-to) specific path command value.
6
- * Usually a `large-arc-flag` or `sweep-flag`.
7
- *
8
- * @param path the `PathParser` instance
9
- */
10
- const scanFlag = (path: PathParser) => {
11
- const { index, pathValue } = path;
12
- const code = pathValue.charCodeAt(index);
13
-
14
- if (code === 0x30 /* 0 */) {
15
- path.param = 0;
16
- path.index += 1;
17
- return;
18
- }
19
-
20
- if (code === 0x31 /* 1 */) {
21
- path.param = 1;
22
- path.index += 1;
23
- return;
24
- }
25
-
26
- path.err = `${error}: invalid Arc flag "${pathValue[index]}", expecting 0 or 1 at index ${index}`;
27
- };
28
-
29
- export default scanFlag;
@@ -1,102 +0,0 @@
1
- import isDigit from './isDigit';
2
- import invalidPathValue from './invalidPathValue';
3
- import error from './error';
4
- import type PathParser from './pathParser';
5
-
6
- /**
7
- * Validates every character of the path string,
8
- * every path command, negative numbers or floating point numbers.
9
- *
10
- * @param path the `PathParser` instance
11
- */
12
- const scanParam = (path: PathParser) => {
13
- const { max, pathValue, index: start } = path;
14
- let index = start;
15
- let zeroFirst = false;
16
- let hasCeiling = false;
17
- let hasDecimal = false;
18
- let hasDot = false;
19
- let ch;
20
-
21
- if (index >= max) {
22
- path.err = `${error}: ${invalidPathValue} at index ${index}, "pathValue" is missing param`;
23
- return;
24
- }
25
- ch = pathValue.charCodeAt(index);
26
-
27
- if (ch === 0x2b /* + */ || ch === 0x2d /* - */) {
28
- index += 1;
29
- // ch = (index < max) ? pathValue.charCodeAt(index) : 0;
30
- ch = pathValue.charCodeAt(index);
31
- }
32
-
33
- // This logic is shamelessly borrowed from Esprima
34
- // https://github.com/ariya/esprimas
35
- if (!isDigit(ch) && ch !== 0x2e /* . */) {
36
- // path.err = 'SvgPath: param should start with 0..9 or `.` (at pos ' + index + ')';
37
- path.err = `${error}: ${invalidPathValue} at index ${index}, "${pathValue[index]}" is not a number`;
38
- return;
39
- }
40
-
41
- if (ch !== 0x2e /* . */) {
42
- zeroFirst = ch === 0x30 /* 0 */;
43
- index += 1;
44
-
45
- ch = pathValue.charCodeAt(index);
46
-
47
- if (zeroFirst && index < max) {
48
- // decimal number starts with '0' such as '09' is illegal.
49
- if (ch && isDigit(ch)) {
50
- // path.err = 'SvgPath: numbers started with `0` such as `09`
51
- // are illegal (at pos ' + start + ')';
52
- path.err = `${error}: ${invalidPathValue} at index ${start}, "${pathValue[start]}" illegal number`;
53
- return;
54
- }
55
- }
56
-
57
- while (index < max && isDigit(pathValue.charCodeAt(index))) {
58
- index += 1;
59
- hasCeiling = true;
60
- }
61
-
62
- ch = pathValue.charCodeAt(index);
63
- }
64
-
65
- if (ch === 0x2e /* . */) {
66
- hasDot = true;
67
- index += 1;
68
- while (isDigit(pathValue.charCodeAt(index))) {
69
- index += 1;
70
- hasDecimal = true;
71
- }
72
-
73
- ch = pathValue.charCodeAt(index);
74
- }
75
-
76
- if (ch === 0x65 /* e */ || ch === 0x45 /* E */) {
77
- if (hasDot && !hasCeiling && !hasDecimal) {
78
- path.err = `${error}: ${invalidPathValue} at index ${index}, "${pathValue[index]}" invalid float exponent`;
79
- return;
80
- }
81
-
82
- index += 1;
83
-
84
- ch = pathValue.charCodeAt(index);
85
-
86
- if (ch === 0x2b /* + */ || ch === 0x2d /* - */) {
87
- index += 1;
88
- }
89
- if (index < max && isDigit(pathValue.charCodeAt(index))) {
90
- while (index < max && isDigit(pathValue.charCodeAt(index))) {
91
- index += 1;
92
- }
93
- } else {
94
- path.err = `${error}: ${invalidPathValue} at index ${index}, "${pathValue[index]}" invalid integer exponent`;
95
- return;
96
- }
97
- }
98
-
99
- path.index = index;
100
- path.param = +path.pathValue.slice(start, index);
101
- };
102
- export default scanParam;
@@ -1,84 +0,0 @@
1
- import finalizeSegment from './finalizeSegment';
2
- import paramCounts from './paramsCount';
3
- import scanFlag from './scanFlag';
4
- import scanParam from './scanParam';
5
- import skipSpaces from './skipSpaces';
6
- import isPathCommand from './isPathCommand';
7
- import isDigitStart from './isDigitStart';
8
- import isArcCommand from './isArcCommand';
9
- import isMoveCommand from './isMoveCommand';
10
- import invalidPathValue from './invalidPathValue';
11
- import error from './error';
12
-
13
- import type PathParser from './pathParser';
14
- import type { PathSegment, RelativeCommand } from '../types';
15
-
16
- /**
17
- * Scans every character in the path string to determine
18
- * where a segment starts and where it ends.
19
- *
20
- * @param path the `PathParser` instance
21
- */
22
- const scanSegment = (path: PathParser) => {
23
- const { max, pathValue, index, segments } = path;
24
- const cmdCode = pathValue.charCodeAt(index);
25
- const reqParams = paramCounts[pathValue[index].toLowerCase() as RelativeCommand];
26
-
27
- path.segmentStart = index;
28
-
29
- // segments always start with a path command
30
- if (!isPathCommand(cmdCode)) {
31
- path.err = `${error}: ${invalidPathValue} "${pathValue[index]}" is not a path command at index ${index}`;
32
- return;
33
- }
34
-
35
- // after a Z segment, we only expect a MoveTo path command
36
- const lastSegment = segments[segments.length - 1] as PathSegment | undefined;
37
- if (!isMoveCommand(cmdCode) && lastSegment?.[0]?.toLocaleLowerCase() === 'z') {
38
- path.err = `${error}: ${invalidPathValue} "${pathValue[index]}" is not a MoveTo path command at index ${index}`;
39
- return;
40
- }
41
-
42
- path.index += 1;
43
- skipSpaces(path);
44
-
45
- path.data = [];
46
-
47
- if (!reqParams) {
48
- // Z
49
- finalizeSegment(path);
50
- return;
51
- }
52
-
53
- for (;;) {
54
- for (let i = reqParams; i > 0; i -= 1) {
55
- if (isArcCommand(cmdCode) && (i === 3 || i === 4)) scanFlag(path);
56
- else scanParam(path);
57
-
58
- if (path.err.length) {
59
- return;
60
- }
61
- path.data.push(path.param);
62
-
63
- skipSpaces(path);
64
-
65
- // after ',' param is mandatory
66
- if (path.index < max && pathValue.charCodeAt(path.index) === 0x2c /* , */) {
67
- path.index += 1;
68
- skipSpaces(path);
69
- }
70
- }
71
-
72
- if (path.index >= path.max) {
73
- break;
74
- }
75
-
76
- // Stop on next segment
77
- if (!isDigitStart(pathValue.charCodeAt(path.index))) {
78
- break;
79
- }
80
- }
81
-
82
- finalizeSegment(path);
83
- };
84
- export default scanSegment;
@@ -1,17 +0,0 @@
1
- import isSpace from './isSpace';
2
- import type PathParser from './pathParser';
3
-
4
- /**
5
- * Points the parser to the next character in the
6
- * path string every time it encounters any kind of
7
- * space character.
8
- *
9
- * @param path the `PathParser` instance
10
- */
11
- const skipSpaces = (path: PathParser) => {
12
- const { pathValue, max } = path;
13
- while (path.index < max && isSpace(pathValue.charCodeAt(path.index))) {
14
- path.index += 1;
15
- }
16
- };
17
- export default skipSpaces;
@@ -1,63 +0,0 @@
1
- import type {
2
- AbsoluteSegment,
3
- AbsoluteCommand,
4
- ASegment,
5
- VSegment,
6
- HSegment,
7
- QSegment,
8
- SSegment,
9
- TSegment,
10
- CSegment,
11
- PathSegment,
12
- MSegment,
13
- LSegment,
14
- } from '../types';
15
-
16
- /**
17
- * Returns an absolute segment of a `PathArray` object.
18
- *
19
- * @param segment the segment object
20
- * @param index the segment index
21
- * @param lastX the last known X value
22
- * @param lastY the last known Y value
23
- * @returns the absolute segment
24
- */
25
- const absolutizeSegment = (segment: PathSegment, index: number, lastX: number, lastY: number) => {
26
- const [pathCommand] = segment;
27
- const absCommand = pathCommand.toUpperCase() as AbsoluteCommand;
28
- const isAbsolute = absCommand === pathCommand;
29
-
30
- /* istanbul ignore else @preserve */
31
- if (index === 0 || isAbsolute) return segment as MSegment | AbsoluteSegment;
32
- // const values = segment.slice(1) as number[];
33
- if (absCommand === 'A') {
34
- return [
35
- absCommand,
36
- segment[1],
37
- segment[2],
38
- segment[3],
39
- segment[4],
40
- segment[5],
41
- (segment as ASegment)[6] + lastX,
42
- (segment as ASegment)[7] + lastY,
43
- ] as ASegment;
44
- } else if (absCommand === 'V') {
45
- return [absCommand, (segment as VSegment)[1] + lastY] as VSegment;
46
- } else if (absCommand === 'H') {
47
- return [absCommand, (segment as HSegment)[1] + lastX] as HSegment;
48
- } else if (absCommand === 'L') {
49
- return [absCommand, (segment as LSegment)[1] + lastX, (segment as LSegment)[2] + lastY] as LSegment;
50
- } else {
51
- // use brakets for `eslint: no-case-declaration`
52
- // https://stackoverflow.com/a/50753272/803358
53
- const absValues = (segment.slice(1) as number[]).map((n, j) => n + (j % 2 ? lastY : lastX));
54
- // for c, s, q, t
55
- return [absCommand as typeof absCommand | number].concat(absValues) as
56
- | MSegment
57
- | QSegment
58
- | TSegment
59
- | SSegment
60
- | CSegment;
61
- }
62
- };
63
- export default absolutizeSegment;
@@ -1,128 +0,0 @@
1
- import rotateVector from '../math/rotateVector';
2
-
3
- /**
4
- * Converts A (arc-to) segments to C (cubic-bezier-to).
5
- *
6
- * For more information of where this math came from visit:
7
- * http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
8
- *
9
- * @param X1 the starting x position
10
- * @param Y1 the starting y position
11
- * @param RX x-radius of the arc
12
- * @param RY y-radius of the arc
13
- * @param angle x-axis-rotation of the arc
14
- * @param LAF large-arc-flag of the arc
15
- * @param SF sweep-flag of the arc
16
- * @param X2 the ending x position
17
- * @param Y2 the ending y position
18
- * @param recursive the parameters needed to split arc into 2 segments
19
- * @return the resulting cubic-bezier segment(s)
20
- */
21
- const arcToCubic = (
22
- X1: number,
23
- Y1: number,
24
- RX: number,
25
- RY: number,
26
- angle: number,
27
- LAF: number,
28
- SF: number,
29
- X2: number,
30
- Y2: number,
31
- recursive?: [number, number, number, number],
32
- ): number[] => {
33
- let x1 = X1;
34
- let y1 = Y1;
35
- let rx = RX;
36
- let ry = RY;
37
- let x2 = X2;
38
- let y2 = Y2;
39
- // for more information of where this Math came from visit:
40
- // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
41
- const d120 = (Math.PI * 120) / 180;
42
-
43
- const rad = (Math.PI / 180) * (+angle || 0);
44
- let res = [] as number[];
45
- let xy;
46
- let f1;
47
- let f2;
48
- let cx;
49
- let cy;
50
-
51
- if (!recursive) {
52
- xy = rotateVector(x1, y1, -rad);
53
- x1 = xy.x;
54
- y1 = xy.y;
55
- xy = rotateVector(x2, y2, -rad);
56
- x2 = xy.x;
57
- y2 = xy.y;
58
-
59
- const x = (x1 - x2) / 2;
60
- const y = (y1 - y2) / 2;
61
- let h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
62
- if (h > 1) {
63
- h = Math.sqrt(h);
64
- rx *= h;
65
- ry *= h;
66
- }
67
- const rx2 = rx * rx;
68
- const ry2 = ry * ry;
69
-
70
- const k =
71
- (LAF === SF ? -1 : 1) *
72
- Math.sqrt(Math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x)));
73
-
74
- cx = (k * rx * y) / ry + (x1 + x2) / 2;
75
- cy = (k * -ry * x) / rx + (y1 + y2) / 2;
76
- // eslint-disable-next-line no-bitwise -- Impossible to satisfy no-bitwise
77
- f1 = Math.asin(((((y1 - cy) / ry) * 10 ** 9) >> 0) / 10 ** 9);
78
- // eslint-disable-next-line no-bitwise -- Impossible to satisfy no-bitwise
79
- f2 = Math.asin(((((y2 - cy) / ry) * 10 ** 9) >> 0) / 10 ** 9);
80
-
81
- f1 = x1 < cx ? Math.PI - f1 : f1;
82
- f2 = x2 < cx ? Math.PI - f2 : f2;
83
- if (f1 < 0) f1 = Math.PI * 2 + f1;
84
- if (f2 < 0) f2 = Math.PI * 2 + f2;
85
- if (SF && f1 > f2) {
86
- f1 -= Math.PI * 2;
87
- }
88
- if (!SF && f2 > f1) {
89
- f2 -= Math.PI * 2;
90
- }
91
- } else {
92
- [f1, f2, cx, cy] = recursive;
93
- }
94
- let df = f2 - f1;
95
- if (Math.abs(df) > d120) {
96
- const f2old = f2;
97
- const x2old = x2;
98
- const y2old = y2;
99
- f2 = f1 + d120 * (SF && f2 > f1 ? 1 : -1);
100
- x2 = cx + rx * Math.cos(f2);
101
- y2 = cy + ry * Math.sin(f2);
102
- res = arcToCubic(x2, y2, rx, ry, angle, 0, SF, x2old, y2old, [f2, f2old, cx, cy]);
103
- }
104
- df = f2 - f1;
105
- const c1 = Math.cos(f1);
106
- const s1 = Math.sin(f1);
107
- const c2 = Math.cos(f2);
108
- const s2 = Math.sin(f2);
109
- const t = Math.tan(df / 4);
110
- const hx = (4 / 3) * rx * t;
111
- const hy = (4 / 3) * ry * t;
112
- const m1 = [x1, y1];
113
- const m2 = [x1 + hx * s1, y1 - hy * c1];
114
- const m3 = [x2 + hx * s2, y2 - hy * c2];
115
- const m4 = [x2, y2];
116
- m2[0] = 2 * m1[0] - m2[0];
117
- m2[1] = 2 * m1[1] - m2[1];
118
- if (recursive) {
119
- return [m2[0], m2[1], m3[0], m3[1], m4[0], m4[1]].concat(res);
120
- }
121
- res = [m2[0], m2[1], m3[0], m3[1], m4[0], m4[1]].concat(res);
122
- const newres = [];
123
- for (let i = 0, ii = res.length; i < ii; i += 1) {
124
- newres[i] = i % 2 ? rotateVector(res[i - 1], res[i], rad).y : rotateVector(res[i], res[i + 1], rad).x;
125
- }
126
- return newres;
127
- };
128
- export default arcToCubic;
@@ -1,70 +0,0 @@
1
- import CSSMatrix from '@thednp/dommatrix';
2
- // import type { TransformObject } from '../interface';
3
- import type { TransformObjectValues } from '../types';
4
-
5
- /**
6
- * Returns a transformation matrix to apply to `<path>` elements.
7
- *
8
- * @see TransformObjectValues
9
- *
10
- * @param transform the `transformObject`
11
- * @returns a new transformation matrix
12
- */
13
- const getSVGMatrix = (transform: TransformObjectValues): CSSMatrix => {
14
- let matrix = new CSSMatrix();
15
- const { origin } = transform;
16
- const [originX, originY] = origin as [number, number, number];
17
- const { translate } = transform;
18
- const { rotate } = transform;
19
- const { skew } = transform;
20
- const { scale } = transform;
21
-
22
- // set translate
23
- if (
24
- Array.isArray(translate) &&
25
- translate.length >= 2 &&
26
- translate.every(x => !Number.isNaN(+x)) &&
27
- translate.some(x => x !== 0)
28
- ) {
29
- matrix = matrix.translate(...(translate as [number, number, number?]));
30
- } else if (typeof translate === 'number' && !Number.isNaN(translate)) {
31
- matrix = matrix.translate(translate);
32
- }
33
-
34
- if (rotate || skew || scale) {
35
- // set SVG transform-origin, always defined
36
- matrix = matrix.translate(originX, originY);
37
-
38
- // set rotation
39
- if (
40
- Array.isArray(rotate) &&
41
- rotate.length >= 2 &&
42
- rotate.every(x => !Number.isNaN(+x)) &&
43
- rotate.some(x => x !== 0)
44
- ) {
45
- matrix = matrix.rotate(...(rotate as [number, number, number?]));
46
- } else if (typeof rotate === 'number' && !Number.isNaN(rotate)) {
47
- matrix = matrix.rotate(rotate);
48
- }
49
-
50
- // set skew(s)
51
- if (Array.isArray(skew) && skew.length === 2 && skew.every(x => !Number.isNaN(+x)) && skew.some(x => x !== 0)) {
52
- matrix = skew[0] ? matrix.skewX(skew[0]) : matrix;
53
- matrix = skew[1] ? matrix.skewY(skew[1]) : matrix;
54
- } else if (typeof skew === 'number' && !Number.isNaN(skew)) {
55
- matrix = matrix.skewX(skew);
56
- }
57
-
58
- // set scale
59
- if (Array.isArray(scale) && scale.length >= 2 && scale.every(x => !Number.isNaN(+x)) && scale.some(x => x !== 1)) {
60
- matrix = matrix.scale(...(scale as [number, number, number?]));
61
- } else if (typeof scale === 'number' && !Number.isNaN(scale)) {
62
- matrix = matrix.scale(scale);
63
- }
64
- // set SVG transform-origin
65
- matrix = matrix.translate(-originX, -originY);
66
- }
67
-
68
- return matrix;
69
- };
70
- export default getSVGMatrix;
@@ -1,58 +0,0 @@
1
- // import paramsParser from '../parser/paramsParser';
2
- import type { PathArray, PathCommand, PathSegment, IteratorCallback, AbsoluteCommand } from '../types';
3
-
4
- const iterate = <T extends PathArray>(path: PathArray, iterator: IteratorCallback) => {
5
- let pathLen = path.length;
6
- let segment: PathSegment;
7
- let pathCommand = 'M' as PathCommand;
8
- let absCommand = 'M' as AbsoluteCommand;
9
- let isRelative = false;
10
- let x = 0;
11
- let y = 0;
12
- let mx = 0;
13
- let my = 0;
14
- let segLen = 0;
15
-
16
- for (let i = 0; i < pathLen; i += 1) {
17
- segment = path[i];
18
- [pathCommand] = segment;
19
- segLen = segment.length;
20
- absCommand = pathCommand.toUpperCase() as AbsoluteCommand;
21
- isRelative = absCommand !== pathCommand;
22
-
23
- const iteratorResult = iterator(segment, i, x, y);
24
- // some methods like getPointAtLength would like to break
25
- // when task is complete
26
- if (iteratorResult === false) {
27
- break;
28
- }
29
-
30
- // segment = path[i];
31
- if (absCommand === 'Z') {
32
- x = mx;
33
- y = my;
34
- } else if (absCommand === 'H') {
35
- x = (segment[1] as number) + (isRelative ? x : 0);
36
- } else if (absCommand === 'V') {
37
- y = (segment[1] as number) + (isRelative ? y : 0);
38
- } else {
39
- x = (segment[segLen - 2] as number) + (isRelative ? x : 0);
40
- y = (segment[segLen - 1] as number) + (isRelative ? y : 0);
41
-
42
- if (absCommand === 'M') {
43
- mx = x;
44
- my = y;
45
- }
46
- }
47
-
48
- if (iteratorResult) {
49
- path[i] = iteratorResult;
50
- if (iteratorResult[0] === 'C') {
51
- pathLen = path.length;
52
- }
53
- }
54
- }
55
- return path as T;
56
- };
57
-
58
- export default iterate;
@@ -1,17 +0,0 @@
1
- import midPoint from '../math/midPoint';
2
-
3
- /**
4
- * Converts an L (line-to) segment to C (cubic-bezier).
5
- *
6
- * @param x1 line start x
7
- * @param y1 line start y
8
- * @param x2 line end x
9
- * @param y2 line end y
10
- * @returns the cubic-bezier segment
11
- */
12
- const lineToCubic = (x1: number, y1: number, x2: number, y2: number) => {
13
- const c1 = midPoint([x1, y1], [x2, y2], 1.0 / 3.0);
14
- const c2 = midPoint([x1, y1], [x2, y2], 2.0 / 3.0);
15
- return [c1[0], c1[1], c2[0], c2[1], x2, y2];
16
- };
17
- export default lineToCubic;
@@ -1,33 +0,0 @@
1
- import normalizeSegment from './normalizeSegment';
2
- import type { NormalArray, PathArray } from '../types';
3
- import iterate from './iterate';
4
- import parsePathString from '../parser/parsePathString';
5
- import paramsParser from '../parser/paramsParser';
6
-
7
- /**
8
- * Normalizes a `pathArray` object for further processing:
9
- * * convert segments to absolute values
10
- * * convert shorthand path commands to their non-shorthand notation
11
- *
12
- * @param pathInput the string to be parsed or 'pathArray'
13
- * @returns the normalized `pathArray`
14
- */
15
- const normalizePath = (pathInput: string | PathArray) => {
16
- const path = parsePathString(pathInput);
17
- const params = { ...paramsParser };
18
-
19
- return iterate<NormalArray>(path, (seg, _, lastX, lastY) => {
20
- params.x = lastX;
21
- params.y = lastY;
22
- const result = normalizeSegment(seg, params);
23
-
24
- const seglen = result.length;
25
- params.x1 = +result[seglen - 2];
26
- params.y1 = +result[seglen - 1];
27
- params.x2 = +result[seglen - 4] || params.x1;
28
- params.y2 = +result[seglen - 3] || params.y1;
29
-
30
- return result;
31
- });
32
- };
33
- export default normalizePath;
@@ -1,84 +0,0 @@
1
- import type { ParserParams } from '../interface';
2
- import type {
3
- NormalSegment,
4
- PointTuple,
5
- PathSegment,
6
- QSegment,
7
- CSegment,
8
- LSegment,
9
- MSegment,
10
- HSegment,
11
- VSegment,
12
- ASegment,
13
- PathCommand,
14
- } from '../types';
15
-
16
- /**
17
- * Normalizes a single segment of a `pathArray` object.
18
- *
19
- * @param segment the segment object
20
- * @param params the normalization parameters
21
- * @returns the normalized segment
22
- */
23
- const normalizeSegment = (segment: PathSegment, params: ParserParams) => {
24
- const [pathCommand] = segment;
25
- const absCommand = pathCommand.toUpperCase();
26
- const isRelative = pathCommand !== absCommand;
27
- const { x1: px1, y1: py1, x2: px2, y2: py2, x, y } = params;
28
- const values = segment.slice(1) as number[];
29
- let absValues = values.map((n, j) => n + (isRelative ? (j % 2 ? y : x) : 0));
30
-
31
- if (!'TQ'.includes(absCommand)) {
32
- // optional but good to be cautious
33
- params.qx = null;
34
- params.qy = null;
35
- }
36
-
37
- // istanbul ignore else @preserve
38
- if (absCommand === 'A') {
39
- absValues = values.slice(0, -2).concat(values[5] + (isRelative ? x : 0), values[6] + (isRelative ? y : 0));
40
-
41
- return ['A' as PathCommand | number].concat(absValues) as ASegment;
42
- } else if (absCommand === 'H') {
43
- return ['L', (segment as HSegment)[1] + (isRelative ? x : 0), py1] as LSegment;
44
- } else if (absCommand === 'V') {
45
- return ['L', px1, (segment as VSegment)[1] + (isRelative ? y : 0)] as LSegment;
46
- } else if (absCommand === 'L') {
47
- return [
48
- 'L',
49
- (segment as LSegment)[1] + (isRelative ? x : 0),
50
- (segment as LSegment)[2] + (isRelative ? y : 0),
51
- ] as LSegment;
52
- } else if (absCommand === 'M') {
53
- return [
54
- 'M',
55
- (segment as MSegment)[1] + (isRelative ? x : 0),
56
- (segment as MSegment)[2] + (isRelative ? y : 0),
57
- ] as MSegment;
58
- } else if (absCommand === 'C') {
59
- return ['C' as PathCommand | number].concat(absValues) as CSegment;
60
- } else if (absCommand === 'S') {
61
- const x1 = px1 * 2 - px2;
62
- const y1 = py1 * 2 - py2;
63
- params.x1 = x1;
64
- params.y1 = y1;
65
- return ['C', x1, y1].concat(absValues) as CSegment;
66
- } else if (absCommand === 'T') {
67
- const qx = px1 * 2 - (params.qx ? params.qx : /* istanbul ignore next */ 0);
68
- const qy = py1 * 2 - (params.qy ? params.qy : /* istanbul ignore next */ 0);
69
- params.qx = qx;
70
- params.qy = qy;
71
- return ['Q', qx, qy].concat(absValues) as QSegment;
72
- } else if (absCommand === 'Q') {
73
- const [nqx, nqy] = absValues as PointTuple;
74
- params.qx = nqx;
75
- params.qy = nqy;
76
- return ['Q' as PathCommand | number].concat(absValues) as QSegment;
77
- } else if (absCommand === 'Z') {
78
- return ['Z'] as NormalSegment;
79
- }
80
-
81
- // istanbul ignore next @preserve
82
- return segment as NormalSegment;
83
- };
84
- export default normalizeSegment;