svg-path-commander 2.1.2 → 2.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +4 -4
  2. package/dist/svg-path-commander.cjs +1 -1
  3. package/dist/svg-path-commander.cjs.map +1 -1
  4. package/dist/svg-path-commander.d.ts +755 -849
  5. package/dist/svg-path-commander.js +1 -1
  6. package/dist/svg-path-commander.js.map +1 -1
  7. package/dist/svg-path-commander.mjs +947 -611
  8. package/dist/svg-path-commander.mjs.map +1 -1
  9. package/package.json +14 -22
  10. package/.eslintrc.cjs +0 -225
  11. package/.prettierrc.json +0 -15
  12. package/dts.config.ts +0 -15
  13. package/src/convert/pathToAbsolute.ts +0 -18
  14. package/src/convert/pathToCurve.ts +0 -43
  15. package/src/convert/pathToRelative.ts +0 -18
  16. package/src/convert/pathToString.ts +0 -50
  17. package/src/index.ts +0 -454
  18. package/src/interface.ts +0 -130
  19. package/src/math/arcTools.ts +0 -396
  20. package/src/math/bezier.ts +0 -261
  21. package/src/math/cubicTools.ts +0 -124
  22. package/src/math/distanceSquareRoot.ts +0 -15
  23. package/src/math/lineTools.ts +0 -69
  24. package/src/math/midPoint.ts +0 -18
  25. package/src/math/polygonTools.ts +0 -48
  26. package/src/math/quadTools.ts +0 -100
  27. package/src/math/rotateVector.ts +0 -17
  28. package/src/math/roundTo.ts +0 -7
  29. package/src/options/options.ts +0 -9
  30. package/src/parser/error.ts +0 -2
  31. package/src/parser/finalizeSegment.ts +0 -35
  32. package/src/parser/invalidPathValue.ts +0 -2
  33. package/src/parser/isArcCommand.ts +0 -11
  34. package/src/parser/isDigit.ts +0 -12
  35. package/src/parser/isDigitStart.ts +0 -14
  36. package/src/parser/isMoveCommand.ts +0 -17
  37. package/src/parser/isPathCommand.ts +0 -28
  38. package/src/parser/isSpace.ts +0 -23
  39. package/src/parser/paramsCount.ts +0 -16
  40. package/src/parser/paramsParser.ts +0 -14
  41. package/src/parser/parsePathString.ts +0 -33
  42. package/src/parser/pathParser.ts +0 -29
  43. package/src/parser/scanFlag.ts +0 -29
  44. package/src/parser/scanParam.ts +0 -102
  45. package/src/parser/scanSegment.ts +0 -84
  46. package/src/parser/skipSpaces.ts +0 -17
  47. package/src/process/absolutizeSegment.ts +0 -63
  48. package/src/process/arcToCubic.ts +0 -128
  49. package/src/process/getSVGMatrix.ts +0 -70
  50. package/src/process/iterate.ts +0 -58
  51. package/src/process/lineToCubic.ts +0 -17
  52. package/src/process/normalizePath.ts +0 -33
  53. package/src/process/normalizeSegment.ts +0 -84
  54. package/src/process/optimizePath.ts +0 -63
  55. package/src/process/projection2d.ts +0 -52
  56. package/src/process/quadToCubic.ts +0 -31
  57. package/src/process/relativizeSegment.ts +0 -59
  58. package/src/process/reverseCurve.ts +0 -24
  59. package/src/process/reversePath.ts +0 -114
  60. package/src/process/roundPath.ts +0 -33
  61. package/src/process/roundSegment.ts +0 -9
  62. package/src/process/segmentToCubic.ts +0 -48
  63. package/src/process/shortenSegment.ts +0 -71
  64. package/src/process/splitCubic.ts +0 -29
  65. package/src/process/splitPath.ts +0 -63
  66. package/src/process/transformPath.ts +0 -110
  67. package/src/types.ts +0 -228
  68. package/src/util/distanceEpsilon.ts +0 -3
  69. package/src/util/getClosestPoint.ts +0 -15
  70. package/src/util/getDrawDirection.ts +0 -16
  71. package/src/util/getPathArea.ts +0 -70
  72. package/src/util/getPathBBox.ts +0 -98
  73. package/src/util/getPointAtLength.ts +0 -110
  74. package/src/util/getPropertiesAtLength.ts +0 -67
  75. package/src/util/getPropertiesAtPoint.ts +0 -84
  76. package/src/util/getSegmentAtLength.ts +0 -15
  77. package/src/util/getSegmentOfPoint.ts +0 -18
  78. package/src/util/getTotalLength.ts +0 -68
  79. package/src/util/isAbsoluteArray.ts +0 -18
  80. package/src/util/isCurveArray.ts +0 -15
  81. package/src/util/isNormalizedArray.ts +0 -16
  82. package/src/util/isPathArray.ts +0 -24
  83. package/src/util/isPointInStroke.ts +0 -16
  84. package/src/util/isRelativeArray.ts +0 -18
  85. package/src/util/isValidPath.ts +0 -27
  86. package/src/util/shapeParams.ts +0 -16
  87. package/src/util/shapeToPath.ts +0 -86
  88. package/src/util/shapeToPathArray.ts +0 -183
  89. package/test/class.test.ts +0 -504
  90. package/test/fixtures/getMarkup.ts +0 -17
  91. package/test/fixtures/shapeObjects.ts +0 -11
  92. package/test/fixtures/shapes.js +0 -104
  93. package/test/fixtures/simpleShapes.js +0 -75
  94. package/test/static.test.ts +0 -330
  95. package/vite.config.mts +0 -41
  96. package/vitest.config-ui.mts +0 -26
  97. package/vitest.config.mts +0 -26
@@ -1,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;
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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;