svg-path-commander 2.1.3 → 2.1.6
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 -830
- package/dist/svg-path-commander.js +1 -1
- package/dist/svg-path-commander.js.map +1 -1
- package/dist/svg-path-commander.mjs +670 -395
- package/dist/svg-path-commander.mjs.map +1 -1
- package/package.json +16 -24
- package/.eslintrc.cjs +0 -225
- package/.lgtm.yml +0 -9
- 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 -388
- package/src/math/bezier.ts +0 -261
- package/src/math/cubicTools.ts +0 -123
- package/src/math/distanceSquareRoot.ts +0 -15
- package/src/math/lineTools.ts +0 -61
- package/src/math/midPoint.ts +0 -18
- package/src/math/polygonTools.ts +0 -48
- package/src/math/quadTools.ts +0 -98
- 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 -67
- 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 -62
- package/src/process/projection2d.ts +0 -52
- package/src/process/quadToCubic.ts +0 -31
- package/src/process/relativizeSegment.ts +0 -63
- 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 -114
- 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 -171
- 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 -133
- 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 -506
- 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 -87
- package/test/static.test.ts +0 -330
- package/tsconfig.json +0 -34
- package/vite.config.mts +0 -41
- package/vitest.config-ui.mts +0 -31
- package/vitest.config.mts +0 -31
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import DISTANCE_EPSILON from './distanceEpsilon';
|
|
2
|
-
import type { MSegment, PathArray, PointTuple } from '../types';
|
|
3
|
-
import iterate from '../process/iterate';
|
|
4
|
-
import { getLineLength, getPointAtLineLength } from '../math/lineTools';
|
|
5
|
-
import { getArcLength, getPointAtArcLength } from '../math/arcTools';
|
|
6
|
-
import { getCubicLength, getPointAtCubicLength } from '../math/cubicTools';
|
|
7
|
-
import { getQuadLength, getPointAtQuadLength } from '../math/quadTools';
|
|
8
|
-
import normalizePath from '../process/normalizePath';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Returns [x,y] coordinates of a point at a given length of a shape.
|
|
12
|
-
*
|
|
13
|
-
* @param pathInput the `pathArray` to look into
|
|
14
|
-
* @param distance the length of the shape to look at
|
|
15
|
-
* @returns the requested {x, y} point coordinates
|
|
16
|
-
*/
|
|
17
|
-
const getPointAtLength = (pathInput: string | PathArray, distance?: number) => {
|
|
18
|
-
const path = normalizePath(pathInput);
|
|
19
|
-
let isM = false;
|
|
20
|
-
let data = [] as number[];
|
|
21
|
-
let pathCommand = 'M';
|
|
22
|
-
let x = 0;
|
|
23
|
-
let y = 0;
|
|
24
|
-
let [mx, my] = path[0].slice(1) as PointTuple;
|
|
25
|
-
const distanceIsNumber = typeof distance === 'number';
|
|
26
|
-
let point = { x: mx, y: my };
|
|
27
|
-
let length = 0;
|
|
28
|
-
let POINT = point;
|
|
29
|
-
let totalLength = 0;
|
|
30
|
-
|
|
31
|
-
if (!distanceIsNumber || distance < DISTANCE_EPSILON) return point;
|
|
32
|
-
|
|
33
|
-
// for (let i = 0; i < pathLen; i += 1) {
|
|
34
|
-
iterate(path, (seg, _, lastX, lastY) => {
|
|
35
|
-
[pathCommand] = seg;
|
|
36
|
-
isM = pathCommand === 'M';
|
|
37
|
-
data = !isM ? [lastX, lastY].concat(seg.slice(1) as number[]) : data;
|
|
38
|
-
|
|
39
|
-
// this segment is always ZERO
|
|
40
|
-
/* istanbul ignore else @preserve */
|
|
41
|
-
if (isM) {
|
|
42
|
-
// remember mx, my for Z
|
|
43
|
-
[, mx, my] = seg as MSegment;
|
|
44
|
-
point = { x: mx, y: my };
|
|
45
|
-
length = 0;
|
|
46
|
-
} else if (pathCommand === 'L') {
|
|
47
|
-
point = getPointAtLineLength(data[0], data[1], data[2], data[3], distance - totalLength);
|
|
48
|
-
length = getLineLength(data[0], data[1], data[2], data[3]);
|
|
49
|
-
} else if (pathCommand === 'A') {
|
|
50
|
-
point = getPointAtArcLength(
|
|
51
|
-
data[0],
|
|
52
|
-
data[1],
|
|
53
|
-
data[2],
|
|
54
|
-
data[3],
|
|
55
|
-
data[4],
|
|
56
|
-
data[5],
|
|
57
|
-
data[6],
|
|
58
|
-
data[7],
|
|
59
|
-
data[8],
|
|
60
|
-
distance - totalLength,
|
|
61
|
-
);
|
|
62
|
-
length = getArcLength(data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8]);
|
|
63
|
-
} else if (pathCommand === 'C') {
|
|
64
|
-
point = getPointAtCubicLength(
|
|
65
|
-
data[0],
|
|
66
|
-
data[1],
|
|
67
|
-
data[2],
|
|
68
|
-
data[3],
|
|
69
|
-
data[4],
|
|
70
|
-
data[5],
|
|
71
|
-
data[6],
|
|
72
|
-
data[7],
|
|
73
|
-
distance - totalLength,
|
|
74
|
-
);
|
|
75
|
-
length = getCubicLength(data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
|
|
76
|
-
} else if (pathCommand === 'Q') {
|
|
77
|
-
point = getPointAtQuadLength(data[0], data[1], data[2], data[3], data[4], data[5], distance - totalLength);
|
|
78
|
-
length = getQuadLength(data[0], data[1], data[2], data[3], data[4], data[5]);
|
|
79
|
-
} else if (pathCommand === 'Z') {
|
|
80
|
-
data = [lastX, lastY, mx, my];
|
|
81
|
-
point = { x: mx, y: my };
|
|
82
|
-
|
|
83
|
-
length = getLineLength(data[0], data[1], data[2], data[3]);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
[x, y] = data.slice(-2);
|
|
87
|
-
|
|
88
|
-
if (totalLength < distance) {
|
|
89
|
-
POINT = point;
|
|
90
|
-
} else {
|
|
91
|
-
// totalLength >= distance
|
|
92
|
-
// stop right here
|
|
93
|
-
// stop iterator now!
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
totalLength += length;
|
|
98
|
-
return;
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
// native `getPointAtLength` behavior when the given distance
|
|
102
|
-
// is higher than total length
|
|
103
|
-
if (distance > totalLength - DISTANCE_EPSILON) {
|
|
104
|
-
return { x, y };
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return POINT;
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
export default getPointAtLength;
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import type { PathArray, PathSegment } from '../types';
|
|
2
|
-
import type { SegmentProperties } from '../interface';
|
|
3
|
-
import parsePathString from '../parser/parsePathString';
|
|
4
|
-
import getTotalLength from './getTotalLength';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Returns the segment, its index and length as well as
|
|
8
|
-
* the length to that segment at a given length in a path.
|
|
9
|
-
*
|
|
10
|
-
* @param pathInput target `pathArray`
|
|
11
|
-
* @param distance the given length
|
|
12
|
-
* @returns the requested properties
|
|
13
|
-
*/
|
|
14
|
-
const getPropertiesAtLength = (pathInput: string | PathArray, distance?: number): SegmentProperties => {
|
|
15
|
-
const pathArray = parsePathString(pathInput);
|
|
16
|
-
|
|
17
|
-
let pathTemp = pathArray.slice(0) as PathArray;
|
|
18
|
-
let pathLength = getTotalLength(pathTemp);
|
|
19
|
-
let index = pathTemp.length - 1;
|
|
20
|
-
let lengthAtSegment = 0;
|
|
21
|
-
let length = 0;
|
|
22
|
-
let segment = pathArray[0] as PathSegment;
|
|
23
|
-
|
|
24
|
-
// If the path is empty, return 0.
|
|
25
|
-
if (index <= 0 || !distance || !Number.isFinite(distance)) {
|
|
26
|
-
return {
|
|
27
|
-
segment,
|
|
28
|
-
index: 0,
|
|
29
|
-
length,
|
|
30
|
-
lengthAtSegment,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (distance >= pathLength) {
|
|
35
|
-
pathTemp = pathArray.slice(0, -1) as PathArray;
|
|
36
|
-
lengthAtSegment = getTotalLength(pathTemp);
|
|
37
|
-
length = pathLength - lengthAtSegment;
|
|
38
|
-
segment = pathArray[index];
|
|
39
|
-
return {
|
|
40
|
-
segment,
|
|
41
|
-
index,
|
|
42
|
-
length,
|
|
43
|
-
lengthAtSegment,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const segments = [] as SegmentProperties[];
|
|
48
|
-
while (index > 0) {
|
|
49
|
-
segment = pathTemp[index];
|
|
50
|
-
pathTemp = pathTemp.slice(0, -1) as PathArray;
|
|
51
|
-
lengthAtSegment = getTotalLength(pathTemp);
|
|
52
|
-
length = pathLength - lengthAtSegment;
|
|
53
|
-
pathLength = lengthAtSegment;
|
|
54
|
-
|
|
55
|
-
segments.push({
|
|
56
|
-
segment,
|
|
57
|
-
index,
|
|
58
|
-
length,
|
|
59
|
-
lengthAtSegment,
|
|
60
|
-
});
|
|
61
|
-
index -= 1;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return segments.find(({ lengthAtSegment: l }) => l <= distance) as SegmentProperties;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export default getPropertiesAtLength;
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import type { PathArray, Point } from '../types';
|
|
2
|
-
import type { PointProperties } from '../interface';
|
|
3
|
-
import getPointAtLength from './getPointAtLength';
|
|
4
|
-
import getPropertiesAtLength from './getPropertiesAtLength';
|
|
5
|
-
import getTotalLength from './getTotalLength';
|
|
6
|
-
import parsePathString from '../parser/parsePathString';
|
|
7
|
-
import normalizePath from '../process/normalizePath';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Returns the point and segment in path closest to a given point as well as
|
|
11
|
-
* the distance to the path stroke.
|
|
12
|
-
*
|
|
13
|
-
* @see https://bl.ocks.org/mbostock/8027637
|
|
14
|
-
*
|
|
15
|
-
* @param pathInput target `pathArray`
|
|
16
|
-
* @param point the given point
|
|
17
|
-
* @returns the requested properties
|
|
18
|
-
*/
|
|
19
|
-
const getPropertiesAtPoint = (pathInput: string | PathArray, point: Point): PointProperties => {
|
|
20
|
-
const path = parsePathString(pathInput);
|
|
21
|
-
const normalPath = normalizePath(path);
|
|
22
|
-
const pathLength = getTotalLength(normalPath);
|
|
23
|
-
const distanceTo = (p: Point) => {
|
|
24
|
-
const dx = p.x - point.x;
|
|
25
|
-
const dy = p.y - point.y;
|
|
26
|
-
return dx * dx + dy * dy;
|
|
27
|
-
};
|
|
28
|
-
let precision = 8;
|
|
29
|
-
let scan: Point;
|
|
30
|
-
let closest = { x: 0, y: 0 }; // make TS happy
|
|
31
|
-
let scanDistance = 0;
|
|
32
|
-
let bestLength = 0;
|
|
33
|
-
let bestDistance = Infinity;
|
|
34
|
-
|
|
35
|
-
// linear scan for coarse approximation
|
|
36
|
-
for (let scanLength = 0; scanLength <= pathLength; scanLength += precision) {
|
|
37
|
-
scan = getPointAtLength(normalPath, scanLength);
|
|
38
|
-
scanDistance = distanceTo(scan);
|
|
39
|
-
|
|
40
|
-
if (scanDistance < bestDistance) {
|
|
41
|
-
closest = scan;
|
|
42
|
-
bestLength = scanLength;
|
|
43
|
-
bestDistance = scanDistance;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// binary search for precise estimate
|
|
48
|
-
precision /= 2;
|
|
49
|
-
let before: { x: number; y: number };
|
|
50
|
-
let after: { x: number; y: number };
|
|
51
|
-
let beforeLength = 0;
|
|
52
|
-
let afterLength = 0;
|
|
53
|
-
let beforeDistance = 0;
|
|
54
|
-
let afterDistance = 0;
|
|
55
|
-
|
|
56
|
-
while (precision > 0.000001) {
|
|
57
|
-
beforeLength = bestLength - precision;
|
|
58
|
-
before = getPointAtLength(normalPath, beforeLength);
|
|
59
|
-
beforeDistance = distanceTo(before);
|
|
60
|
-
afterLength = bestLength + precision;
|
|
61
|
-
after = getPointAtLength(normalPath, afterLength);
|
|
62
|
-
afterDistance = distanceTo(after);
|
|
63
|
-
|
|
64
|
-
if (beforeLength >= 0 && beforeDistance < bestDistance) {
|
|
65
|
-
closest = before;
|
|
66
|
-
bestLength = beforeLength;
|
|
67
|
-
bestDistance = beforeDistance;
|
|
68
|
-
} else if (afterLength <= pathLength && afterDistance < bestDistance) {
|
|
69
|
-
closest = after;
|
|
70
|
-
bestLength = afterLength;
|
|
71
|
-
bestDistance = afterDistance;
|
|
72
|
-
} else {
|
|
73
|
-
precision /= 2;
|
|
74
|
-
}
|
|
75
|
-
if (precision < 0.00001) break;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const segment = getPropertiesAtLength(path, bestLength);
|
|
79
|
-
const distance = Math.sqrt(bestDistance);
|
|
80
|
-
|
|
81
|
-
return { closest, distance, segment };
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
export default getPropertiesAtPoint;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { PathArray, PathSegment } from '../types';
|
|
2
|
-
import getPropertiesAtLength from './getPropertiesAtLength';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Returns the segment at a given length.
|
|
6
|
-
*
|
|
7
|
-
* @param pathInput the target `pathArray`
|
|
8
|
-
* @param distance the distance in path to look at
|
|
9
|
-
* @returns the requested segment
|
|
10
|
-
*/
|
|
11
|
-
const getSegmentAtLength = (pathInput: string | PathArray, distance?: number): PathSegment | undefined => {
|
|
12
|
-
return getPropertiesAtLength(pathInput, distance).segment;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export default getSegmentAtLength;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { SegmentProperties } from '../interface';
|
|
2
|
-
import type { PathArray } from '../types';
|
|
3
|
-
import getPropertiesAtPoint from './getPropertiesAtPoint';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Returns the path segment which contains a given point.
|
|
7
|
-
*
|
|
8
|
-
* @param path the `pathArray` to look into
|
|
9
|
-
* @param point the point of the shape to look for
|
|
10
|
-
* @returns the requested segment
|
|
11
|
-
*/
|
|
12
|
-
const getSegmentOfPoint = (
|
|
13
|
-
path: string | PathArray,
|
|
14
|
-
point: { x: number; y: number },
|
|
15
|
-
): SegmentProperties | undefined => {
|
|
16
|
-
return getPropertiesAtPoint(path, point).segment;
|
|
17
|
-
};
|
|
18
|
-
export default getSegmentOfPoint;
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import type { LSegment, MSegment, PathArray, PointTuple } from '../types';
|
|
2
|
-
import { getLineLength } from '../math/lineTools';
|
|
3
|
-
import { getArcLength } from '../math/arcTools';
|
|
4
|
-
import { getCubicLength } from '../math/cubicTools';
|
|
5
|
-
import { getQuadLength } from '../math/quadTools';
|
|
6
|
-
import iterate from '../process/iterate';
|
|
7
|
-
import parsePathString from '../parser/parsePathString';
|
|
8
|
-
import absolutizeSegment from '../process/absolutizeSegment';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Returns the shape total length, or the equivalent to `shape.getTotalLength()`.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* @param pathInput the target `pathArray`
|
|
15
|
-
* @returns the shape total length
|
|
16
|
-
*/
|
|
17
|
-
const getTotalLength = (pathInput: string | PathArray) => {
|
|
18
|
-
const path = parsePathString(pathInput);
|
|
19
|
-
let paramX1 = 0;
|
|
20
|
-
let paramY1 = 0;
|
|
21
|
-
let paramX2 = 0;
|
|
22
|
-
let paramY2 = 0;
|
|
23
|
-
let paramQX = 0;
|
|
24
|
-
let paramQY = 0;
|
|
25
|
-
let pathCommand = 'M';
|
|
26
|
-
let mx = 0;
|
|
27
|
-
let my = 0;
|
|
28
|
-
let totalLength = 0;
|
|
29
|
-
|
|
30
|
-
iterate(path, (seg, index, lastX, lastY) => {
|
|
31
|
-
[pathCommand] = seg;
|
|
32
|
-
const absCommand = pathCommand.toUpperCase();
|
|
33
|
-
const isRelative = absCommand !== pathCommand;
|
|
34
|
-
const absoluteSegment = isRelative ? absolutizeSegment(seg, index, lastX, lastY) : (seg.slice(0) as typeof seg);
|
|
35
|
-
|
|
36
|
-
const normalSegment =
|
|
37
|
-
absCommand === 'V'
|
|
38
|
-
? (['L', lastX, absoluteSegment[1]] as LSegment)
|
|
39
|
-
: absCommand === 'H'
|
|
40
|
-
? (['L', absoluteSegment[1], lastY] as LSegment)
|
|
41
|
-
: absoluteSegment;
|
|
42
|
-
[pathCommand] = normalSegment;
|
|
43
|
-
|
|
44
|
-
if (!'TQ'.includes(absCommand)) {
|
|
45
|
-
// optional but good to be cautious
|
|
46
|
-
paramQX = 0;
|
|
47
|
-
paramQY = 0;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// this segment is always ZERO
|
|
51
|
-
/* istanbul ignore else @preserve */
|
|
52
|
-
if (pathCommand === 'M') {
|
|
53
|
-
// remember mx, my for Z
|
|
54
|
-
[, mx, my] = normalSegment as MSegment;
|
|
55
|
-
} else if (pathCommand === 'L') {
|
|
56
|
-
totalLength += getLineLength(lastX, lastY, normalSegment[1] as number, normalSegment[2] as number);
|
|
57
|
-
} else if (pathCommand === 'A') {
|
|
58
|
-
totalLength += getArcLength(
|
|
59
|
-
lastX,
|
|
60
|
-
lastY,
|
|
61
|
-
normalSegment[1] as number,
|
|
62
|
-
normalSegment[2] as number,
|
|
63
|
-
normalSegment[3] as number,
|
|
64
|
-
normalSegment[4] as number,
|
|
65
|
-
normalSegment[5] as number,
|
|
66
|
-
normalSegment[6] as number,
|
|
67
|
-
normalSegment[7] as number,
|
|
68
|
-
);
|
|
69
|
-
} else if (pathCommand === 'S') {
|
|
70
|
-
const cp1x = paramX1 * 2 - paramX2;
|
|
71
|
-
const cp1y = paramY1 * 2 - paramY2;
|
|
72
|
-
|
|
73
|
-
totalLength += getCubicLength(
|
|
74
|
-
lastX,
|
|
75
|
-
lastY,
|
|
76
|
-
cp1x,
|
|
77
|
-
cp1y,
|
|
78
|
-
normalSegment[1] as number,
|
|
79
|
-
normalSegment[2] as number,
|
|
80
|
-
normalSegment[3] as number,
|
|
81
|
-
normalSegment[4] as number,
|
|
82
|
-
);
|
|
83
|
-
} else if (pathCommand === 'C') {
|
|
84
|
-
totalLength += getCubicLength(
|
|
85
|
-
lastX,
|
|
86
|
-
lastY,
|
|
87
|
-
normalSegment[1] as number,
|
|
88
|
-
normalSegment[2] as number,
|
|
89
|
-
normalSegment[3] as number,
|
|
90
|
-
normalSegment[4] as number,
|
|
91
|
-
normalSegment[5] as number,
|
|
92
|
-
normalSegment[6] as number,
|
|
93
|
-
);
|
|
94
|
-
} else if (pathCommand === 'T') {
|
|
95
|
-
paramQX = paramX1 * 2 - paramQX;
|
|
96
|
-
paramQY = paramY1 * 2 - paramQY;
|
|
97
|
-
totalLength += getQuadLength(
|
|
98
|
-
lastX,
|
|
99
|
-
lastY,
|
|
100
|
-
paramQX,
|
|
101
|
-
paramQY,
|
|
102
|
-
normalSegment[1] as number,
|
|
103
|
-
normalSegment[2] as number,
|
|
104
|
-
);
|
|
105
|
-
} else if (pathCommand === 'Q') {
|
|
106
|
-
paramQX = normalSegment[1] as number;
|
|
107
|
-
paramQY = normalSegment[2] as number;
|
|
108
|
-
totalLength += getQuadLength(
|
|
109
|
-
lastX,
|
|
110
|
-
lastY,
|
|
111
|
-
normalSegment[1] as number,
|
|
112
|
-
normalSegment[2] as number,
|
|
113
|
-
normalSegment[3] as number,
|
|
114
|
-
normalSegment[4] as number,
|
|
115
|
-
);
|
|
116
|
-
} else if (pathCommand === 'Z') {
|
|
117
|
-
totalLength += getLineLength(lastX, lastY, mx, my);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// update params
|
|
121
|
-
[paramX1, paramY1] = pathCommand === 'Z' ? [mx, my] : (normalSegment.slice(-2) as PointTuple);
|
|
122
|
-
[paramX2, paramY2] =
|
|
123
|
-
pathCommand === 'C'
|
|
124
|
-
? ([normalSegment[3], normalSegment[4]] as PointTuple)
|
|
125
|
-
: pathCommand === 'S'
|
|
126
|
-
? ([normalSegment[1], normalSegment[2]] as PointTuple)
|
|
127
|
-
: [paramX1, paramY1];
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
return totalLength;
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
export default getTotalLength;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { AbsoluteArray } from '../types';
|
|
2
|
-
import isPathArray from './isPathArray';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Iterates an array to check if it's a `pathArray`
|
|
6
|
-
* with all absolute values.
|
|
7
|
-
*
|
|
8
|
-
* @param path the `pathArray` to be checked
|
|
9
|
-
* @returns iteration result
|
|
10
|
-
*/
|
|
11
|
-
const isAbsoluteArray = (path: unknown): path is AbsoluteArray => {
|
|
12
|
-
return (
|
|
13
|
-
isPathArray(path) &&
|
|
14
|
-
// `isPathArray` also checks if it's `Array`
|
|
15
|
-
path.every(([x]) => x === x.toUpperCase())
|
|
16
|
-
);
|
|
17
|
-
};
|
|
18
|
-
export default isAbsoluteArray;
|
package/src/util/isCurveArray.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { CurveArray } from '../types';
|
|
2
|
-
import isNormalizedArray from './isNormalizedArray';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Iterates an array to check if it's a `pathArray`
|
|
6
|
-
* with all C (cubic bezier) segments.
|
|
7
|
-
*
|
|
8
|
-
* @param path the `Array` to be checked
|
|
9
|
-
* @returns iteration result
|
|
10
|
-
*/
|
|
11
|
-
const isCurveArray = (path: unknown): path is CurveArray => {
|
|
12
|
-
// `isPathArray` also checks if it's `Array`
|
|
13
|
-
return isNormalizedArray(path) && path.every(([pc]) => 'MC'.includes(pc));
|
|
14
|
-
};
|
|
15
|
-
export default isCurveArray;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { NormalArray } from '../types';
|
|
2
|
-
import isAbsoluteArray from './isAbsoluteArray';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Iterates an array to check if it's a `pathArray`
|
|
6
|
-
* with all segments are in non-shorthand notation
|
|
7
|
-
* with absolute values.
|
|
8
|
-
*
|
|
9
|
-
* @param {string | SVGPath.pathArray} path the `pathArray` to be checked
|
|
10
|
-
* @returns {boolean} iteration result
|
|
11
|
-
*/
|
|
12
|
-
const isNormalizedArray = (path: unknown): path is NormalArray => {
|
|
13
|
-
// `isAbsoluteArray` also checks if it's `Array`
|
|
14
|
-
return isAbsoluteArray(path) && path.every(([pc]) => 'ACLMQZ'.includes(pc));
|
|
15
|
-
};
|
|
16
|
-
export default isNormalizedArray;
|
package/src/util/isPathArray.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { PathArray, PathSegment, RelativeCommand } from '../types';
|
|
2
|
-
import paramsCount from '../parser/paramsCount';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Iterates an array to check if it's an actual `pathArray`.
|
|
6
|
-
*
|
|
7
|
-
* @param path the `pathArray` to be checked
|
|
8
|
-
* @returns iteration result
|
|
9
|
-
*/
|
|
10
|
-
const isPathArray = (path: unknown): path is PathArray => {
|
|
11
|
-
return (
|
|
12
|
-
Array.isArray(path) &&
|
|
13
|
-
path.every((seg: PathSegment) => {
|
|
14
|
-
const lk = seg[0].toLowerCase() as RelativeCommand;
|
|
15
|
-
return (
|
|
16
|
-
paramsCount[lk] === seg.length - 1 &&
|
|
17
|
-
'achlmqstvz'.includes(lk) &&
|
|
18
|
-
(seg.slice(1) as unknown[]).every(Number.isFinite)
|
|
19
|
-
);
|
|
20
|
-
}) &&
|
|
21
|
-
path.length > 0
|
|
22
|
-
);
|
|
23
|
-
};
|
|
24
|
-
export default isPathArray;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { PathArray } from '../types';
|
|
2
|
-
import getPropertiesAtPoint from './getPropertiesAtPoint';
|
|
3
|
-
import DISTANCE_EPSILON from './distanceEpsilon';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Checks if a given point is in the stroke of a path.
|
|
7
|
-
*
|
|
8
|
-
* @param pathInput target path
|
|
9
|
-
* @param point the given `{x,y}` point
|
|
10
|
-
* @returns the query result
|
|
11
|
-
*/
|
|
12
|
-
const isPointInStroke = (pathInput: string | PathArray, point: { x: number; y: number }) => {
|
|
13
|
-
const { distance } = getPropertiesAtPoint(pathInput, point);
|
|
14
|
-
return Math.abs(distance) < DISTANCE_EPSILON; // 0.01 might be more permissive
|
|
15
|
-
};
|
|
16
|
-
export default isPointInStroke;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { RelativeArray } from '../types';
|
|
2
|
-
import isPathArray from './isPathArray';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Iterates an array to check if it's a `pathArray`
|
|
6
|
-
* with relative values.
|
|
7
|
-
*
|
|
8
|
-
* @param path the `pathArray` to be checked
|
|
9
|
-
* @returns iteration result
|
|
10
|
-
*/
|
|
11
|
-
const isRelativeArray = (path: unknown): path is RelativeArray => {
|
|
12
|
-
return (
|
|
13
|
-
isPathArray(path) &&
|
|
14
|
-
// `isPathArray` checks if it's `Array`
|
|
15
|
-
path.slice(1).every(([pc]) => pc === pc.toLowerCase())
|
|
16
|
-
);
|
|
17
|
-
};
|
|
18
|
-
export default isRelativeArray;
|
package/src/util/isValidPath.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import scanSegment from '../parser/scanSegment';
|
|
2
|
-
import skipSpaces from '../parser/skipSpaces';
|
|
3
|
-
import PathParser from '../parser/pathParser';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Parses a path string value to determine its validity
|
|
7
|
-
* then returns true if it's valid or false otherwise.
|
|
8
|
-
*
|
|
9
|
-
* @param pathString the path string to be parsed
|
|
10
|
-
* @returns the path string validity
|
|
11
|
-
*/
|
|
12
|
-
const isValidPath = (pathString: string) => {
|
|
13
|
-
if (typeof pathString !== 'string' || !pathString.length) {
|
|
14
|
-
return false;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const path = new PathParser(pathString);
|
|
18
|
-
|
|
19
|
-
skipSpaces(path);
|
|
20
|
-
|
|
21
|
-
while (path.index < path.max && !path.err.length) {
|
|
22
|
-
scanSegment(path);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return !path.err.length && 'mM'.includes(path.segments[0][0]);
|
|
26
|
-
};
|
|
27
|
-
export default isValidPath;
|
package/src/util/shapeParams.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { ShapeParams } from '../interface';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Supported shapes and their specific parameters.
|
|
5
|
-
*/
|
|
6
|
-
const shapeParams: ShapeParams = {
|
|
7
|
-
line: ['x1', 'y1', 'x2', 'y2'],
|
|
8
|
-
circle: ['cx', 'cy', 'r'],
|
|
9
|
-
ellipse: ['cx', 'cy', 'rx', 'ry'],
|
|
10
|
-
rect: ['width', 'height', 'x', 'y', 'rx', 'ry'],
|
|
11
|
-
polygon: ['points'],
|
|
12
|
-
polyline: ['points'],
|
|
13
|
-
glyph: ['d'],
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export default shapeParams;
|
package/src/util/shapeToPath.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import type { ShapeParams } from '../interface';
|
|
2
|
-
import type { ShapeOps, ShapeTypes } from '../types';
|
|
3
|
-
import pathToString from '../convert/pathToString';
|
|
4
|
-
import defaultOptions from '../options/options';
|
|
5
|
-
import error from '../parser/error';
|
|
6
|
-
import isValidPath from './isValidPath';
|
|
7
|
-
import shapeToPathArray from './shapeToPathArray';
|
|
8
|
-
import shapeParams from './shapeParams';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Returns a new `<path>` element created from attributes of a `<line>`, `<polyline>`,
|
|
12
|
-
* `<polygon>`, `<rect>`, `<ellipse>`, `<circle>` or `<glyph>`. If `replace` parameter
|
|
13
|
-
* is `true`, it will replace the target. The default `ownerDocument` is your current
|
|
14
|
-
* `document` browser page, if you want to use in server-side using `jsdom`, you can
|
|
15
|
-
* pass the `jsdom` `document` to `ownDocument`.
|
|
16
|
-
*
|
|
17
|
-
* It can also work with an options object, see the type below
|
|
18
|
-
*
|
|
19
|
-
* @see ShapeOps
|
|
20
|
-
*
|
|
21
|
-
* The newly created `<path>` element keeps all non-specific
|
|
22
|
-
* attributes like `class`, `fill`, etc.
|
|
23
|
-
*
|
|
24
|
-
* @param element target shape
|
|
25
|
-
* @param replace option to replace target
|
|
26
|
-
* @param ownerDocument document for create element
|
|
27
|
-
* @return the newly created `<path>` element
|
|
28
|
-
*/
|
|
29
|
-
const shapeToPath = (
|
|
30
|
-
element: ShapeTypes | ShapeOps,
|
|
31
|
-
replace?: boolean,
|
|
32
|
-
ownerDocument?: Document,
|
|
33
|
-
): SVGPathElement | false => {
|
|
34
|
-
const doc = ownerDocument || document;
|
|
35
|
-
const win = doc.defaultView || /* istanbul ignore next */ window;
|
|
36
|
-
const supportedShapes = Object.keys(shapeParams) as (keyof ShapeParams)[];
|
|
37
|
-
const targetIsElement = element instanceof win.SVGElement;
|
|
38
|
-
const tagName = targetIsElement ? element.tagName : null;
|
|
39
|
-
|
|
40
|
-
if (tagName === 'path') throw TypeError(`${error}: "${tagName}" is already SVGPathElement`);
|
|
41
|
-
if (tagName && supportedShapes.every(s => tagName !== s)) throw TypeError(`${error}: "${tagName}" is not SVGElement`);
|
|
42
|
-
|
|
43
|
-
const path = doc.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
44
|
-
const type = (targetIsElement ? tagName : element.type) as ShapeOps['type'];
|
|
45
|
-
const shapeAttrs = shapeParams[type] as string[];
|
|
46
|
-
const config = { type } as Record<string, string>;
|
|
47
|
-
|
|
48
|
-
// set d
|
|
49
|
-
const round = defaultOptions.round as number;
|
|
50
|
-
const pathArray = shapeToPathArray(element, doc);
|
|
51
|
-
const description = pathArray && pathArray.length ? pathToString(pathArray, round) : '';
|
|
52
|
-
|
|
53
|
-
if (targetIsElement) {
|
|
54
|
-
shapeAttrs.forEach(p => {
|
|
55
|
-
config[p] = element.getAttribute(p) as string;
|
|
56
|
-
});
|
|
57
|
-
// set no-specific shape attributes: fill, stroke, etc
|
|
58
|
-
Object.values(element.attributes).forEach(({ name, value }) => {
|
|
59
|
-
if (!shapeAttrs.includes(name)) path.setAttribute(name, value);
|
|
60
|
-
});
|
|
61
|
-
} else {
|
|
62
|
-
Object.assign(config, element);
|
|
63
|
-
// set no-specific shape attributes: fill, stroke, etc
|
|
64
|
-
Object.keys(config).forEach(k => {
|
|
65
|
-
if (!shapeAttrs.includes(k) && k !== 'type') {
|
|
66
|
-
path.setAttribute(
|
|
67
|
-
k.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`),
|
|
68
|
-
config[k],
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// replace target element
|
|
75
|
-
if (isValidPath(description)) {
|
|
76
|
-
path.setAttribute('d', description);
|
|
77
|
-
if (replace && targetIsElement) {
|
|
78
|
-
element.before(path, element);
|
|
79
|
-
element.remove();
|
|
80
|
-
}
|
|
81
|
-
return path;
|
|
82
|
-
}
|
|
83
|
-
return false;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
export default shapeToPath;
|