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.
- package/README.md +4 -4
- package/dist/svg-path-commander.cjs +1 -1
- package/dist/svg-path-commander.cjs.map +1 -1
- package/dist/svg-path-commander.d.ts +755 -849
- package/dist/svg-path-commander.js +1 -1
- package/dist/svg-path-commander.js.map +1 -1
- package/dist/svg-path-commander.mjs +947 -611
- package/dist/svg-path-commander.mjs.map +1 -1
- package/package.json +14 -22
- package/.eslintrc.cjs +0 -225
- package/.prettierrc.json +0 -15
- package/dts.config.ts +0 -15
- package/src/convert/pathToAbsolute.ts +0 -18
- package/src/convert/pathToCurve.ts +0 -43
- package/src/convert/pathToRelative.ts +0 -18
- package/src/convert/pathToString.ts +0 -50
- package/src/index.ts +0 -454
- package/src/interface.ts +0 -130
- package/src/math/arcTools.ts +0 -396
- package/src/math/bezier.ts +0 -261
- package/src/math/cubicTools.ts +0 -124
- package/src/math/distanceSquareRoot.ts +0 -15
- package/src/math/lineTools.ts +0 -69
- package/src/math/midPoint.ts +0 -18
- package/src/math/polygonTools.ts +0 -48
- package/src/math/quadTools.ts +0 -100
- package/src/math/rotateVector.ts +0 -17
- package/src/math/roundTo.ts +0 -7
- package/src/options/options.ts +0 -9
- package/src/parser/error.ts +0 -2
- package/src/parser/finalizeSegment.ts +0 -35
- package/src/parser/invalidPathValue.ts +0 -2
- package/src/parser/isArcCommand.ts +0 -11
- package/src/parser/isDigit.ts +0 -12
- package/src/parser/isDigitStart.ts +0 -14
- package/src/parser/isMoveCommand.ts +0 -17
- package/src/parser/isPathCommand.ts +0 -28
- package/src/parser/isSpace.ts +0 -23
- package/src/parser/paramsCount.ts +0 -16
- package/src/parser/paramsParser.ts +0 -14
- package/src/parser/parsePathString.ts +0 -33
- package/src/parser/pathParser.ts +0 -29
- package/src/parser/scanFlag.ts +0 -29
- package/src/parser/scanParam.ts +0 -102
- package/src/parser/scanSegment.ts +0 -84
- package/src/parser/skipSpaces.ts +0 -17
- package/src/process/absolutizeSegment.ts +0 -63
- package/src/process/arcToCubic.ts +0 -128
- package/src/process/getSVGMatrix.ts +0 -70
- package/src/process/iterate.ts +0 -58
- package/src/process/lineToCubic.ts +0 -17
- package/src/process/normalizePath.ts +0 -33
- package/src/process/normalizeSegment.ts +0 -84
- package/src/process/optimizePath.ts +0 -63
- package/src/process/projection2d.ts +0 -52
- package/src/process/quadToCubic.ts +0 -31
- package/src/process/relativizeSegment.ts +0 -59
- package/src/process/reverseCurve.ts +0 -24
- package/src/process/reversePath.ts +0 -114
- package/src/process/roundPath.ts +0 -33
- package/src/process/roundSegment.ts +0 -9
- package/src/process/segmentToCubic.ts +0 -48
- package/src/process/shortenSegment.ts +0 -71
- package/src/process/splitCubic.ts +0 -29
- package/src/process/splitPath.ts +0 -63
- package/src/process/transformPath.ts +0 -110
- package/src/types.ts +0 -228
- package/src/util/distanceEpsilon.ts +0 -3
- package/src/util/getClosestPoint.ts +0 -15
- package/src/util/getDrawDirection.ts +0 -16
- package/src/util/getPathArea.ts +0 -70
- package/src/util/getPathBBox.ts +0 -98
- package/src/util/getPointAtLength.ts +0 -110
- package/src/util/getPropertiesAtLength.ts +0 -67
- package/src/util/getPropertiesAtPoint.ts +0 -84
- package/src/util/getSegmentAtLength.ts +0 -15
- package/src/util/getSegmentOfPoint.ts +0 -18
- package/src/util/getTotalLength.ts +0 -68
- package/src/util/isAbsoluteArray.ts +0 -18
- package/src/util/isCurveArray.ts +0 -15
- package/src/util/isNormalizedArray.ts +0 -16
- package/src/util/isPathArray.ts +0 -24
- package/src/util/isPointInStroke.ts +0 -16
- package/src/util/isRelativeArray.ts +0 -18
- package/src/util/isValidPath.ts +0 -27
- package/src/util/shapeParams.ts +0 -16
- package/src/util/shapeToPath.ts +0 -86
- package/src/util/shapeToPathArray.ts +0 -183
- package/test/class.test.ts +0 -504
- package/test/fixtures/getMarkup.ts +0 -17
- package/test/fixtures/shapeObjects.ts +0 -11
- package/test/fixtures/shapes.js +0 -104
- package/test/fixtures/simpleShapes.js +0 -75
- package/test/static.test.ts +0 -330
- package/vite.config.mts +0 -41
- package/vitest.config-ui.mts +0 -26
- package/vitest.config.mts +0 -26
package/src/parser/scanFlag.ts
DELETED
|
@@ -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;
|
package/src/parser/scanParam.ts
DELETED
|
@@ -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;
|
package/src/parser/skipSpaces.ts
DELETED
|
@@ -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;
|
package/src/process/iterate.ts
DELETED
|
@@ -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;
|