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
|
@@ -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,68 +0,0 @@
|
|
|
1
|
-
import type { MSegment, PathArray } 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 normalizePath from '../process/normalizePath';
|
|
8
|
-
import parsePathString from '../parser/parsePathString';
|
|
9
|
-
import paramsParser from '../parser/paramsParser';
|
|
10
|
-
import normalizeSegment from '../process/normalizeSegment';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Returns the shape total length, or the equivalent to `shape.getTotalLength()`.
|
|
14
|
-
*
|
|
15
|
-
* The `normalizePath` version is lighter, faster, more efficient and more accurate
|
|
16
|
-
* with paths that are not `curveArray`.
|
|
17
|
-
*
|
|
18
|
-
* @param pathInput the target `pathArray`
|
|
19
|
-
* @returns the shape total length
|
|
20
|
-
*/
|
|
21
|
-
const getTotalLength = (pathInput: string | PathArray) => {
|
|
22
|
-
const path = parsePathString(pathInput);
|
|
23
|
-
const params = { ...paramsParser };
|
|
24
|
-
|
|
25
|
-
let isM = false;
|
|
26
|
-
let data = [] as number[];
|
|
27
|
-
let pathCommand = 'M';
|
|
28
|
-
let mx = 0;
|
|
29
|
-
let my = 0;
|
|
30
|
-
let totalLength = 0;
|
|
31
|
-
|
|
32
|
-
iterate(path, (seg, _, lastX, lastY) => {
|
|
33
|
-
params.x = lastX;
|
|
34
|
-
params.y = lastY;
|
|
35
|
-
const normalSegment = normalizeSegment(seg, params);
|
|
36
|
-
[pathCommand] = normalSegment;
|
|
37
|
-
isM = pathCommand === 'M';
|
|
38
|
-
data = !isM ? [lastX, lastY].concat(normalSegment.slice(1) as number[]) : data;
|
|
39
|
-
|
|
40
|
-
// this segment is always ZERO
|
|
41
|
-
/* istanbul ignore else @preserve */
|
|
42
|
-
if (isM) {
|
|
43
|
-
// remember mx, my for Z
|
|
44
|
-
[, mx, my] = normalSegment as MSegment;
|
|
45
|
-
} else if (pathCommand === 'L') {
|
|
46
|
-
totalLength += getLineLength(data[0], data[1], data[2], data[3]);
|
|
47
|
-
} else if (pathCommand === 'A') {
|
|
48
|
-
totalLength += getArcLength(data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8]);
|
|
49
|
-
} else if (pathCommand === 'C') {
|
|
50
|
-
totalLength += getCubicLength(data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
|
|
51
|
-
} else if (pathCommand === 'Q') {
|
|
52
|
-
totalLength += getQuadLength(data[0], data[1], data[2], data[3], data[4], data[5]);
|
|
53
|
-
} else if (pathCommand === 'Z') {
|
|
54
|
-
data = [lastX, lastY, mx, my];
|
|
55
|
-
totalLength += getLineLength(data[0], data[1], data[2], data[3]);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const seglen = normalSegment.length;
|
|
59
|
-
params.x1 = +normalSegment[seglen - 2];
|
|
60
|
-
params.y1 = +normalSegment[seglen - 1];
|
|
61
|
-
params.x2 = +normalSegment[seglen - 4] || params.x1;
|
|
62
|
-
params.y2 = +normalSegment[seglen - 3] || params.y1;
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
return totalLength;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
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;
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
import type { CircleAttr, EllipseAttr, GlyphAttr, LineAttr, PolyAttr, RectAttr, ShapeParams } from '../interface';
|
|
2
|
-
import type { PathArray, PathSegment, ShapeOps, ShapeTypes } from '../types';
|
|
3
|
-
import error from '../parser/error';
|
|
4
|
-
import parsePathString from '../parser/parsePathString';
|
|
5
|
-
import shapeParams from './shapeParams';
|
|
6
|
-
import isPathArray from './isPathArray';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Returns a new `pathArray` from line attributes.
|
|
10
|
-
*
|
|
11
|
-
* @param attr shape configuration
|
|
12
|
-
* @returns a new line `pathArray`
|
|
13
|
-
*/
|
|
14
|
-
export const getLinePath = (attr: LineAttr): PathArray => {
|
|
15
|
-
let { x1, y1, x2, y2 } = attr;
|
|
16
|
-
[x1, y1, x2, y2] = [x1, y1, x2, y2].map(a => +a);
|
|
17
|
-
return [
|
|
18
|
-
['M', x1, y1],
|
|
19
|
-
['L', x2, y2],
|
|
20
|
-
];
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Returns a new `pathArray` like from polyline/polygon attributes.
|
|
25
|
-
*
|
|
26
|
-
* @param attr shape configuration
|
|
27
|
-
* @return a new polygon/polyline `pathArray`
|
|
28
|
-
*/
|
|
29
|
-
export const getPolyPath = (attr: PolyAttr): PathArray => {
|
|
30
|
-
const pathArray = [] as PathSegment[];
|
|
31
|
-
const points = (attr.points || '')
|
|
32
|
-
.trim()
|
|
33
|
-
.split(/[\s|,]/)
|
|
34
|
-
.map(a => +a);
|
|
35
|
-
|
|
36
|
-
let index = 0;
|
|
37
|
-
while (index < points.length) {
|
|
38
|
-
pathArray.push([index ? 'L' : 'M', points[index], points[index + 1]]);
|
|
39
|
-
index += 2;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return (attr.type === 'polygon' ? [...pathArray, ['z']] : pathArray) as PathArray;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Returns a new `pathArray` from circle attributes.
|
|
47
|
-
*
|
|
48
|
-
* @param attr shape configuration
|
|
49
|
-
* @return a circle `pathArray`
|
|
50
|
-
*/
|
|
51
|
-
export const getCirclePath = (attr: CircleAttr): PathArray => {
|
|
52
|
-
let { cx, cy, r } = attr;
|
|
53
|
-
[cx, cy, r] = [cx, cy, r].map(a => +a);
|
|
54
|
-
|
|
55
|
-
return [
|
|
56
|
-
['M', cx - r, cy],
|
|
57
|
-
['a', r, r, 0, 1, 0, 2 * r, 0],
|
|
58
|
-
['a', r, r, 0, 1, 0, -2 * r, 0],
|
|
59
|
-
];
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Returns a new `pathArray` from ellipse attributes.
|
|
64
|
-
*
|
|
65
|
-
* @param attr shape configuration
|
|
66
|
-
* @return an ellipse `pathArray`
|
|
67
|
-
*/
|
|
68
|
-
export const getEllipsePath = (attr: EllipseAttr): PathArray => {
|
|
69
|
-
let { cx, cy } = attr;
|
|
70
|
-
let rx = attr.rx || 0;
|
|
71
|
-
let ry = attr.ry || rx;
|
|
72
|
-
[cx, cy, rx, ry] = [cx, cy, rx, ry].map(a => +a);
|
|
73
|
-
|
|
74
|
-
return [
|
|
75
|
-
['M', cx - rx, cy],
|
|
76
|
-
['a', rx, ry, 0, 1, 0, 2 * rx, 0],
|
|
77
|
-
['a', rx, ry, 0, 1, 0, -2 * rx, 0],
|
|
78
|
-
];
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Returns a new `pathArray` like from rect attributes.
|
|
83
|
-
*
|
|
84
|
-
* @param attr object with properties above
|
|
85
|
-
* @return a new `pathArray` from `<rect>` attributes
|
|
86
|
-
*/
|
|
87
|
-
export const getRectanglePath = (attr: RectAttr): PathArray => {
|
|
88
|
-
const x = +attr.x || 0;
|
|
89
|
-
const y = +attr.y || 0;
|
|
90
|
-
const w = +attr.width;
|
|
91
|
-
const h = +attr.height;
|
|
92
|
-
let rx = +(attr.rx || 0);
|
|
93
|
-
let ry = +(attr.ry || rx);
|
|
94
|
-
|
|
95
|
-
// Validity checks from http://www.w3.org/TR/SVG/shapes.html#RectElement:
|
|
96
|
-
if (rx || ry) {
|
|
97
|
-
// rx = !rx ? ry : rx;
|
|
98
|
-
// ry = !ry ? rx : ry;
|
|
99
|
-
|
|
100
|
-
/* istanbul ignore else @preserve */
|
|
101
|
-
if (rx * 2 > w) rx -= (rx * 2 - w) / 2;
|
|
102
|
-
/* istanbul ignore else @preserve */
|
|
103
|
-
if (ry * 2 > h) ry -= (ry * 2 - h) / 2;
|
|
104
|
-
|
|
105
|
-
return [
|
|
106
|
-
['M', x + rx, y],
|
|
107
|
-
['h', w - rx * 2],
|
|
108
|
-
['s', rx, 0, rx, ry],
|
|
109
|
-
['v', h - ry * 2],
|
|
110
|
-
['s', 0, ry, -rx, ry],
|
|
111
|
-
['h', -w + rx * 2],
|
|
112
|
-
['s', -rx, 0, -rx, -ry],
|
|
113
|
-
['v', -h + ry * 2],
|
|
114
|
-
['s', 0, -ry, rx, -ry],
|
|
115
|
-
];
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return [['M', x, y], ['h', w], ['v', h], ['H', x], ['Z']];
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Returns a new `pathArray` created from attributes of a `<line>`, `<polyline>`,
|
|
123
|
-
* `<polygon>`, `<rect>`, `<ellipse>`, `<circle>`, <path> or `<glyph>`.
|
|
124
|
-
*
|
|
125
|
-
* The default `ownerDocument` is your current `document` browser page,
|
|
126
|
-
* if you want to use in server-side using `jsdom`, you can pass the
|
|
127
|
-
* `jsdom` `document` to `ownDocument`.
|
|
128
|
-
*
|
|
129
|
-
* It can also work with an options object, see the type below
|
|
130
|
-
*
|
|
131
|
-
* @see ShapeOps
|
|
132
|
-
*
|
|
133
|
-
* @param element target shape
|
|
134
|
-
* @param ownerDocument document for create element
|
|
135
|
-
* @return the newly created `<path>` element
|
|
136
|
-
*/
|
|
137
|
-
const shapeToPathArray = (element: ShapeTypes | ShapeOps, ownerDocument?: Document) => {
|
|
138
|
-
const doc = ownerDocument || document;
|
|
139
|
-
const win = doc.defaultView || /* istanbul ignore next */ window;
|
|
140
|
-
const supportedShapes = Object.keys(shapeParams) as (keyof ShapeParams)[];
|
|
141
|
-
const targetIsElement = element instanceof win.SVGElement;
|
|
142
|
-
const tagName = targetIsElement ? element.tagName : null;
|
|
143
|
-
|
|
144
|
-
if (tagName && [...supportedShapes, 'path'].every(s => tagName !== s)) {
|
|
145
|
-
throw TypeError(`${error}: "${tagName}" is not SVGElement`);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const type = (targetIsElement ? tagName : element.type) as ShapeOps['type'];
|
|
149
|
-
const shapeAttrs = shapeParams[type] as string[];
|
|
150
|
-
const config = { type } as Record<string, string>;
|
|
151
|
-
|
|
152
|
-
if (targetIsElement) {
|
|
153
|
-
shapeAttrs.forEach(p => {
|
|
154
|
-
config[p] = element.getAttribute(p) as string;
|
|
155
|
-
});
|
|
156
|
-
} else {
|
|
157
|
-
Object.assign(config, element);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// set d
|
|
161
|
-
let pathArray = [] as unknown as PathArray;
|
|
162
|
-
|
|
163
|
-
/* istanbul ignore else */
|
|
164
|
-
if (type === 'circle') pathArray = getCirclePath(config as unknown as CircleAttr);
|
|
165
|
-
else if (type === 'ellipse') pathArray = getEllipsePath(config as unknown as EllipseAttr);
|
|
166
|
-
else if (['polyline', 'polygon'].includes(type)) pathArray = getPolyPath(config as unknown as PolyAttr);
|
|
167
|
-
else if (type === 'rect') pathArray = getRectanglePath(config as unknown as RectAttr);
|
|
168
|
-
else if (type === 'line') pathArray = getLinePath(config as unknown as LineAttr);
|
|
169
|
-
else if (['glyph', 'path'].includes(type)) {
|
|
170
|
-
pathArray = parsePathString(
|
|
171
|
-
targetIsElement
|
|
172
|
-
? element.getAttribute('d') || /* istanbul ignore next @preserve */ ''
|
|
173
|
-
: (element as GlyphAttr).d || '',
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// replace target element
|
|
178
|
-
if (isPathArray(pathArray) && pathArray.length) {
|
|
179
|
-
return pathArray;
|
|
180
|
-
}
|
|
181
|
-
return false;
|
|
182
|
-
};
|
|
183
|
-
export default shapeToPathArray;
|