svg-path-commander 1.0.5 → 2.0.0-alpha2

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 (131) hide show
  1. package/README.md +26 -4
  2. package/dist/svg-path-commander.cjs +2 -0
  3. package/dist/svg-path-commander.cjs.map +1 -0
  4. package/dist/svg-path-commander.d.ts +454 -0
  5. package/dist/svg-path-commander.js +2 -3762
  6. package/dist/svg-path-commander.js.map +1 -0
  7. package/dist/svg-path-commander.mjs +1194 -0
  8. package/dist/svg-path-commander.mjs.map +1 -0
  9. package/package.json +39 -42
  10. package/src/convert/pathToAbsolute.ts +101 -0
  11. package/src/convert/{pathToCurve.js → pathToCurve.ts} +12 -14
  12. package/src/convert/pathToRelative.ts +92 -0
  13. package/src/convert/pathToString.ts +17 -0
  14. package/src/{svg-path-commander.js → index.ts} +143 -66
  15. package/src/interface.ts +129 -0
  16. package/src/math/distanceSquareRoot.ts +13 -0
  17. package/src/math/midPoint.ts +16 -0
  18. package/src/math/{polygonArea.js → polygonArea.ts} +6 -4
  19. package/src/math/{polygonLength.js → polygonLength.ts} +5 -3
  20. package/src/math/rotateVector.ts +16 -0
  21. package/src/options/options.ts +9 -0
  22. package/src/parser/error.ts +2 -0
  23. package/src/parser/finalizeSegment.ts +31 -0
  24. package/src/parser/{invalidPathValue.js → invalidPathValue.ts} +0 -0
  25. package/src/parser/{isArcCommand.js → isArcCommand.ts} +5 -4
  26. package/src/parser/isDigit.ts +12 -0
  27. package/src/parser/isDigitStart.ts +14 -0
  28. package/src/parser/isPathCommand.ts +28 -0
  29. package/src/parser/isSpace.ts +23 -0
  30. package/src/parser/paramsCount.ts +16 -0
  31. package/src/parser/paramsParser.ts +14 -0
  32. package/src/parser/{parsePathString.js → parsePathString.ts} +13 -7
  33. package/src/parser/pathParser.ts +29 -0
  34. package/src/parser/{scanFlag.js → scanFlag.ts} +8 -5
  35. package/src/parser/{scanParam.js → scanParam.ts} +12 -11
  36. package/src/parser/{scanSegment.js → scanSegment.ts} +9 -5
  37. package/src/parser/{skipSpaces.js → skipSpaces.ts} +5 -3
  38. package/src/process/{arcToCubic.js → arcToCubic.ts} +42 -28
  39. package/src/process/fixArc.ts +23 -0
  40. package/src/process/getSVGMatrix.ts +70 -0
  41. package/src/process/lineToCubic.ts +17 -0
  42. package/src/process/{normalizePath.js → normalizePath.ts} +10 -10
  43. package/src/process/normalizeSegment.ts +47 -0
  44. package/src/process/{optimizePath.js → optimizePath.ts} +18 -21
  45. package/src/process/{projection2d.js → projection2d.ts} +16 -15
  46. package/src/process/quadToCubic.ts +31 -0
  47. package/src/process/reverseCurve.ts +21 -0
  48. package/src/process/reversePath.ts +101 -0
  49. package/src/process/roundPath.ts +29 -0
  50. package/src/process/segmentToCubic.ts +46 -0
  51. package/src/process/shortenSegment.ts +79 -0
  52. package/src/process/splitCubic.ts +28 -0
  53. package/src/process/{splitPath.js → splitPath.ts} +9 -8
  54. package/src/process/{transformPath.js → transformPath.ts} +55 -48
  55. package/src/types.ts +193 -0
  56. package/src/util/getClosestPoint.ts +15 -0
  57. package/src/util/{getDrawDirection.js → getDrawDirection.ts} +7 -4
  58. package/src/util/getPathArea.ts +70 -0
  59. package/src/util/{getPathBBox.js → getPathBBox.ts} +16 -5
  60. package/src/util/getPointAtLength.ts +14 -0
  61. package/src/util/{getPropertiesAtLength.js → getPropertiesAtLength.ts} +28 -19
  62. package/src/util/{getPropertiesAtPoint.js → getPropertiesAtPoint.ts} +16 -12
  63. package/src/util/getSegmentAtLength.ts +15 -0
  64. package/src/util/getSegmentOfPoint.ts +18 -0
  65. package/src/util/{getTotalLength.js → getTotalLength.ts} +6 -4
  66. package/src/util/isAbsoluteArray.ts +18 -0
  67. package/src/util/{isCurveArray.js → isCurveArray.ts} +6 -4
  68. package/src/util/{isNormalizedArray.js → isNormalizedArray.ts} +4 -2
  69. package/src/util/isPathArray.ts +19 -0
  70. package/src/util/isPointInStroke.ts +15 -0
  71. package/src/util/isRelativeArray.ts +18 -0
  72. package/src/util/{isValidPath.js → isValidPath.ts} +5 -4
  73. package/src/util/{pathLengthFactory.js → pathLengthFactory.ts} +38 -31
  74. package/src/util/{segmentArcFactory.js → segmentArcFactory.ts} +71 -55
  75. package/src/util/segmentCubicFactory.ts +114 -0
  76. package/src/util/{segmentLineFactory.js → segmentLineFactory.ts} +10 -8
  77. package/src/util/{segmentQuadFactory.js → segmentQuadFactory.ts} +47 -32
  78. package/src/util/shapeToPath.ts +214 -0
  79. package/dist/svg-path-commander.es5.js +0 -3875
  80. package/dist/svg-path-commander.es5.min.js +0 -2
  81. package/dist/svg-path-commander.esm.js +0 -3754
  82. package/dist/svg-path-commander.esm.min.js +0 -2
  83. package/dist/svg-path-commander.min.js +0 -2
  84. package/src/convert/pathToAbsolute.js +0 -86
  85. package/src/convert/pathToRelative.js +0 -84
  86. package/src/convert/pathToString.js +0 -14
  87. package/src/index.js +0 -10
  88. package/src/math/distanceSquareRoot.js +0 -14
  89. package/src/math/epsilon.js +0 -8
  90. package/src/math/midPoint.js +0 -13
  91. package/src/math/rotateVector.js +0 -14
  92. package/src/options/options.js +0 -10
  93. package/src/parser/error.js +0 -2
  94. package/src/parser/finalizeSegment.js +0 -28
  95. package/src/parser/isDigit.js +0 -9
  96. package/src/parser/isDigitStart.js +0 -13
  97. package/src/parser/isPathCommand.js +0 -25
  98. package/src/parser/isSpace.js +0 -16
  99. package/src/parser/paramsCount.js +0 -9
  100. package/src/parser/paramsParser.js +0 -8
  101. package/src/parser/pathParser.js +0 -24
  102. package/src/process/clonePath.js +0 -9
  103. package/src/process/fixArc.js +0 -21
  104. package/src/process/fixPath.js +0 -31
  105. package/src/process/getSVGMatrix.js +0 -61
  106. package/src/process/lineToCubic.js +0 -30
  107. package/src/process/normalizeSegment.js +0 -45
  108. package/src/process/quadToCubic.js +0 -22
  109. package/src/process/reverseCurve.js +0 -18
  110. package/src/process/reversePath.js +0 -89
  111. package/src/process/roundPath.js +0 -26
  112. package/src/process/segmentToCubic.js +0 -46
  113. package/src/process/shortenSegment.js +0 -58
  114. package/src/process/splitCubic.js +0 -26
  115. package/src/util/getClosestPoint.js +0 -12
  116. package/src/util/getPathArea.js +0 -47
  117. package/src/util/getPointAtLength.js +0 -12
  118. package/src/util/getSegmentAtLength.js +0 -11
  119. package/src/util/getSegmentOfPoint.js +0 -12
  120. package/src/util/isAbsoluteArray.js +0 -14
  121. package/src/util/isPathArray.js +0 -14
  122. package/src/util/isPointInStroke.js +0 -13
  123. package/src/util/isRelativeArray.js +0 -14
  124. package/src/util/segmentCubicFactory.js +0 -97
  125. package/src/util/shapeToPath.js +0 -204
  126. package/src/util/util.js +0 -82
  127. package/src/version.js +0 -8
  128. package/types/index.d.ts +0 -120
  129. package/types/more/modules.ts +0 -82
  130. package/types/more/svg.d.ts +0 -211
  131. package/types/svg-path-commander.d.ts +0 -1089
@@ -0,0 +1,46 @@
1
+ import arcToCubic from './arcToCubic';
2
+ import quadToCubic from './quadToCubic';
3
+ import lineToCubic from './lineToCubic';
4
+ import type { CSegment, MSegment, PathSegment } from '../types';
5
+ import type { ParserParams } from '../interface';
6
+
7
+ /**
8
+ * Converts any segment to C (cubic-bezier).
9
+ *
10
+ * @param segment the source segment
11
+ * @param params the source segment parameters
12
+ * @returns the cubic-bezier segment
13
+ */
14
+ const segmentToCubic = (segment: PathSegment, params: ParserParams): MSegment | CSegment => {
15
+ const [pathCommand] = segment;
16
+ const values = segment.slice(1).map(Number);
17
+ const [x, y] = values;
18
+ let args;
19
+ const { x1: px1, y1: py1, x: px, y: py } = params;
20
+
21
+ if (!'TQ'.includes(pathCommand)) {
22
+ params.qx = null;
23
+ params.qy = null;
24
+ }
25
+
26
+ if (pathCommand === 'M') {
27
+ params.x = x;
28
+ params.y = y;
29
+ return segment;
30
+ } else if (pathCommand === 'A') {
31
+ args = [px1, py1, ...values] as [number, number, number, number, number, number, number, number, number];
32
+ return ['C', ...arcToCubic(...args)] as CSegment;
33
+ } else if (pathCommand === 'Q') {
34
+ params.qx = x;
35
+ params.qy = y;
36
+ args = [px1, py1, ...values] as [number, number, number, number, number, number];
37
+ return ['C', ...quadToCubic(...args)] as CSegment;
38
+ } else if (pathCommand === 'L') {
39
+ return ['C', ...lineToCubic(px1, py1, x, y)] as CSegment;
40
+ } else if (pathCommand === 'Z') {
41
+ return ['C', ...lineToCubic(px1, py1, px, py)] as CSegment;
42
+ }
43
+
44
+ return segment as MSegment | CSegment;
45
+ };
46
+ export default segmentToCubic;
@@ -0,0 +1,79 @@
1
+ import type { ParserParams } from 'src/interface';
2
+ import type {
3
+ AbsoluteSegment,
4
+ HSegment,
5
+ NormalSegment,
6
+ PathCommand,
7
+ ShortSegment,
8
+ SSegment,
9
+ TSegment,
10
+ VSegment,
11
+ ZSegment,
12
+ } from '../types';
13
+
14
+ /**
15
+ * Shorten a single segment of a `pathArray` object.
16
+ *
17
+ * @param segment the `absoluteSegment` object
18
+ * @param normalSegment the `normalSegment` object
19
+ * @param params the coordinates of the previous segment
20
+ * @param prevCommand the path command of the previous segment
21
+ * @returns the shortened segment
22
+ */
23
+ const shortenSegment = (
24
+ segment: AbsoluteSegment,
25
+ normalSegment: NormalSegment,
26
+ params: ParserParams,
27
+ prevCommand: PathCommand,
28
+ ): ShortSegment => {
29
+ const [pathCommand] = segment;
30
+ const round4 = (n: number) => Math.round(n * 10 ** 4) / 10 ** 4;
31
+ const segmentValues = segment.slice(1).map(n => +n);
32
+ const normalValues = normalSegment.slice(1).map(n => +n);
33
+ const { x1: px1, y1: py1, x2: px2, y2: py2, x: px, y: py } = params;
34
+ let result = segment;
35
+ const [x, y] = normalValues.slice(-2);
36
+
37
+ if (!'TQ'.includes(pathCommand)) {
38
+ // optional but good to be cautious
39
+ params.qx = null;
40
+ params.qy = null;
41
+ }
42
+
43
+ if (['V', 'H', 'S', 'T', 'Z'].includes(pathCommand)) {
44
+ result = [pathCommand, ...segmentValues] as VSegment | HSegment | SSegment | TSegment | ZSegment;
45
+ } else if (pathCommand === 'L') {
46
+ if (round4(px) === round4(x)) {
47
+ result = ['V', y];
48
+ } else if (round4(py) === round4(y)) {
49
+ result = ['H', x];
50
+ }
51
+ } else if (pathCommand === 'C') {
52
+ const [x1, y1] = normalValues;
53
+
54
+ if (
55
+ 'CS'.includes(prevCommand) &&
56
+ ((round4(x1) === round4(px1 * 2 - px2) && round4(y1) === round4(py1 * 2 - py2)) ||
57
+ (round4(px1) === round4(px2 * 2 - px) && round4(py1) === round4(py2 * 2 - py)))
58
+ ) {
59
+ result = ['S', ...normalValues.slice(-4)] as SSegment;
60
+ }
61
+ params.x1 = x1;
62
+ params.y1 = y1;
63
+ } else if (pathCommand === 'Q') {
64
+ const [qx, qy] = normalValues;
65
+ params.qx = qx;
66
+ params.qy = qy;
67
+
68
+ if (
69
+ 'QT'.includes(prevCommand) &&
70
+ ((round4(qx) === round4(px1 * 2 - px2) && round4(qy) === round4(py1 * 2 - py2)) ||
71
+ (round4(px1) === round4(px2 * 2 - px) && round4(py1) === round4(py2 * 2 - py)))
72
+ ) {
73
+ result = ['T', ...normalValues.slice(-2)] as TSegment;
74
+ }
75
+ }
76
+
77
+ return result as ShortSegment;
78
+ };
79
+ export default shortenSegment;
@@ -0,0 +1,28 @@
1
+ import midPoint from '../math/midPoint';
2
+ import type { CubicSegment } from '../types';
3
+
4
+ /**
5
+ * Split a cubic-bezier segment into two.
6
+ *
7
+ * @param pts the cubic-bezier parameters
8
+ * @return two new cubic-bezier segments
9
+ */
10
+ const splitCubic = (pts: number[] /* , ratio */): [CubicSegment, CubicSegment] => {
11
+ const t = /* ratio || */ 0.5;
12
+ const p0 = pts.slice(0, 2) as [number, number];
13
+ const p1 = pts.slice(2, 4) as [number, number];
14
+ const p2 = pts.slice(4, 6) as [number, number];
15
+ const p3 = pts.slice(6, 8) as [number, number];
16
+ const p4 = midPoint(p0, p1, t);
17
+ const p5 = midPoint(p1, p2, t);
18
+ const p6 = midPoint(p2, p3, t);
19
+ const p7 = midPoint(p4, p5, t);
20
+ const p8 = midPoint(p5, p6, t);
21
+ const p9 = midPoint(p7, p8, t);
22
+
23
+ return [
24
+ ['C', ...p4, ...p7, ...p9],
25
+ ['C', ...p8, ...p6, ...p3],
26
+ ];
27
+ };
28
+ export default splitCubic;
@@ -1,20 +1,20 @@
1
+ import type { PathArray } from '../types';
2
+
1
3
  /**
2
4
  * Split a path into an `Array` of sub-path strings.
3
5
  *
4
6
  * In the process, values are converted to absolute
5
7
  * for visual consistency.
6
8
  *
7
- * @param {SVGPath.pathArray} pathInput the source `pathArray`
9
+ * @param pathInput the source `pathArray`
8
10
  * @return {SVGPath.pathArray[]} an array with all sub-path strings
9
11
  */
10
- export default function splitPath(pathInput) {
11
- /** @type {SVGPath.pathArray[]} */
12
- const composite = [];
13
- /** @type {SVGPath.pathArray} */
14
- let path;
12
+ const splitPath = (pathInput: PathArray): PathArray[] => {
13
+ const composite = [] as PathArray[];
14
+ let path: PathArray;
15
15
  let pi = -1;
16
16
 
17
- pathInput.forEach((seg) => {
17
+ pathInput.forEach(seg => {
18
18
  if (seg[0] === 'M') {
19
19
  path = [seg];
20
20
  pi += 1;
@@ -25,4 +25,5 @@ export default function splitPath(pathInput) {
25
25
  });
26
26
 
27
27
  return composite;
28
- }
28
+ };
29
+ export default splitPath;
@@ -1,5 +1,4 @@
1
1
  import normalizePath from './normalizePath';
2
- import clonePath from './clonePath';
3
2
  import pathToAbsolute from '../convert/pathToAbsolute';
4
3
  import segmentToCubic from './segmentToCubic';
5
4
  import fixArc from './fixArc';
@@ -7,6 +6,8 @@ import getSVGMatrix from './getSVGMatrix';
7
6
  import projection2d from './projection2d';
8
7
  import paramsParser from '../parser/paramsParser';
9
8
  import defaultOptions from '../options/options';
9
+ import type { PathArray, PathCommand, TransformObjectValues } from '../types';
10
+ import type { PathTransform, TransformObject } from '../interface';
10
11
 
11
12
  /**
12
13
  * Apply a 2D / 3D transformation to a `pathArray` instance.
@@ -14,17 +15,24 @@ import defaultOptions from '../options/options';
14
15
  * Since *SVGElement* doesn't support 3D transformation, this function
15
16
  * creates a 2D projection of the <path> element.
16
17
  *
17
- * @param {SVGPath.pathArray} path the `pathArray` to apply transformation
18
- * @param {SVGPath.transformObject} transform the transform functions `Object`
19
- * @returns {SVGPath.pathArray} the resulted `pathArray`
18
+ * @param path the `pathArray` to apply transformation
19
+ * @param transform the transform functions `Object`
20
+ * @returns the resulted `pathArray`
20
21
  */
21
- export default function transformPath(path, transform) {
22
- let x = 0; let y = 0; let i; let j; let ii; let jj; let lx; let ly;
22
+ const transformPath = (path: string | PathArray, transform?: Partial<TransformObject>): PathArray => {
23
+ let x = 0;
24
+ let y = 0;
25
+ let i;
26
+ let j;
27
+ let ii;
28
+ let jj;
29
+ let lx;
30
+ let ly;
23
31
  const absolutePath = pathToAbsolute(path);
24
32
  const transformProps = transform && Object.keys(transform);
25
33
 
26
34
  // when used as a static method, invalidate somehow
27
- if (!transform || !transformProps.length) return clonePath(absolutePath);
35
+ if (!transform || (transformProps && !transformProps.length)) return [...absolutePath];
28
36
 
29
37
  const normalizedPath = normalizePath(absolutePath);
30
38
  // transform origin is extremely important
@@ -32,16 +40,14 @@ export default function transformPath(path, transform) {
32
40
  const { origin: defaultOrigin } = defaultOptions;
33
41
  Object.assign(transform, { origin: defaultOrigin });
34
42
  }
35
- const matrixInstance = getSVGMatrix(transform);
43
+ const matrixInstance = getSVGMatrix(transform as TransformObjectValues);
36
44
  const { origin } = transform;
37
45
  const params = { ...paramsParser };
38
- /** @type {SVGPath.pathSegment} */
39
46
  let segment = [];
40
47
  let seglen = 0;
41
48
  let pathCommand = '';
42
- /** @type {SVGPath.pathTransformList[]} */
43
- let transformedPath = [];
44
- const allPathCommands = []; // needed for arc to curve transformation
49
+ let transformedPath = [] as PathTransform[];
50
+ const allPathCommands = [] as PathCommand[]; // needed for arc to curve transformation
45
51
 
46
52
  if (!matrixInstance.isIdentity) {
47
53
  for (i = 0, ii = absolutePath.length; i < ii; i += 1) {
@@ -54,7 +60,7 @@ export default function transformPath(path, transform) {
54
60
  // we don't have any scripting know-how on 3d ellipse transformation
55
61
  // Arc segments don't work 3D transformations or skews
56
62
  /// ////////////////////////////////////////
57
- allPathCommands[i] = pathCommand;
63
+ allPathCommands[i] = pathCommand as PathCommand;
58
64
 
59
65
  if (pathCommand === 'A') {
60
66
  segment = segmentToCubic(normalizedPath[i], params);
@@ -73,49 +79,50 @@ export default function transformPath(path, transform) {
73
79
 
74
80
  params.x1 = +segment[seglen - 2];
75
81
  params.y1 = +segment[seglen - 1];
76
- params.x2 = +(segment[seglen - 4]) || params.x1;
77
- params.y2 = +(segment[seglen - 3]) || params.y1;
82
+ params.x2 = +segment[seglen - 4] || params.x1;
83
+ params.y2 = +segment[seglen - 3] || params.y1;
78
84
 
79
- /** @type {SVGPath.pathTransformList} */
80
85
  const result = {
81
- s: absolutePath[i], c: absolutePath[i][0], x: params.x1, y: params.y1,
86
+ s: absolutePath[i],
87
+ c: absolutePath[i][0],
88
+ x: params.x1,
89
+ y: params.y1,
82
90
  };
83
91
 
84
92
  transformedPath = [...transformedPath, ...[result]];
85
93
  }
86
94
 
87
- return transformedPath.map((seg) => {
95
+ return transformedPath.map(seg => {
88
96
  pathCommand = seg.c;
89
97
  segment = seg.s;
90
- switch (pathCommand) {
91
- case 'L':
92
- case 'H':
93
- case 'V':
94
- [lx, ly] = projection2d(matrixInstance, [seg.x, seg.y], origin);
95
-
96
- /* istanbul ignore else */
97
- if (x !== lx && y !== ly) {
98
- segment = ['L', lx, ly];
99
- } else if (y === ly) {
100
- segment = ['H', lx];
101
- } else if (x === lx) {
102
- segment = ['V', ly];
103
- }
104
-
105
- x = lx; y = ly; // now update x and y
106
-
107
- return segment;
108
- default:
109
-
110
- for (j = 1, jj = segment.length; j < jj; j += 2) {
111
- [x, y] = projection2d(matrixInstance, [+segment[j], +segment[j + 1]], origin);
112
- segment[j] = x;
113
- segment[j + 1] = y;
114
- }
115
-
116
- return segment;
98
+ if (pathCommand === 'L' || pathCommand === 'H' || pathCommand === 'V') {
99
+ [lx, ly] = projection2d(matrixInstance, [seg.x, seg.y], origin as [number, number, number]);
100
+
101
+ /* istanbul ignore else */
102
+ if (x !== lx && y !== ly) {
103
+ segment = ['L', lx, ly];
104
+ } else if (y === ly) {
105
+ segment = ['H', lx];
106
+ } else if (x === lx) {
107
+ segment = ['V', ly];
108
+ }
109
+
110
+ // now update x and y
111
+ x = lx;
112
+ y = ly;
113
+
114
+ return segment;
115
+ } else {
116
+ for (j = 1, jj = segment.length; j < jj; j += 2) {
117
+ [x, y] = projection2d(matrixInstance, [+segment[j], +segment[j + 1]], origin as [number, number, number]);
118
+ segment[j] = x;
119
+ segment[j + 1] = y;
120
+ }
121
+
122
+ return segment;
117
123
  }
118
- });
124
+ }) as PathArray;
119
125
  }
120
- return clonePath(absolutePath);
121
- }
126
+ return [...absolutePath];
127
+ };
128
+ export default transformPath;
package/src/types.ts ADDED
@@ -0,0 +1,193 @@
1
+ import type { LineAttr, CircleAttr, PolyAttr, RectAttr, EllipseAttr, GlyphAttr, TransformObject } from './interface';
2
+
3
+ export type SpaceNumber =
4
+ | 0x1680
5
+ | 0x180e
6
+ | 0x2000
7
+ | 0x2001
8
+ | 0x2002
9
+ | 0x2003
10
+ | 0x2004
11
+ | 0x2005
12
+ | 0x2006
13
+ | 0x2007
14
+ | 0x2008
15
+ | 0x2009
16
+ | 0x200a
17
+ | 0x202f
18
+ | 0x205f
19
+ | 0x3000
20
+ | 0xfeff
21
+ | 0x0a
22
+ | 0x0d
23
+ | 0x2028
24
+ | 0x2029
25
+ | 0x20
26
+ | 0x09
27
+ | 0x0b
28
+ | 0x0c
29
+ | 0xa0
30
+ | 0x1680;
31
+
32
+ export type PathCommandNumber = 0x6d | 0x7a | 0x6c | 0x68 | 0x76 | 0x63 | 0x73 | 0x71 | 0x74 | 0x61;
33
+
34
+ export type DigitNumber = 0x30 | 0x31 | 0x32 | 0x33 | 0x34 | 0x35 | 0x36 | 0x37 | 0x38 | 0x39;
35
+
36
+ // custom types
37
+ export type MCommand = 'M';
38
+ export type mCommand = 'm';
39
+
40
+ export type LCommand = 'L';
41
+ export type lCommand = 'l';
42
+
43
+ export type VCommand = 'V';
44
+ export type vCommand = 'v';
45
+
46
+ export type HCommand = 'H';
47
+ export type hCommand = 'h';
48
+
49
+ export type ZCommand = 'Z';
50
+ export type zCommand = 'z';
51
+
52
+ export type CCommand = 'C';
53
+ export type cCommand = 'c';
54
+
55
+ export type SCommand = 'S';
56
+ export type sCommand = 's';
57
+
58
+ export type QCommand = 'Q';
59
+ export type qCommand = 'q';
60
+
61
+ export type TCommand = 'T';
62
+ export type tCommand = 't';
63
+
64
+ export type ACommand = 'A';
65
+ export type aCommand = 'a';
66
+
67
+ export type AbsoluteCommand =
68
+ | MCommand
69
+ | LCommand
70
+ | VCommand
71
+ | HCommand
72
+ | ZCommand
73
+ | CCommand
74
+ | SCommand
75
+ | QCommand
76
+ | TCommand
77
+ | ACommand;
78
+
79
+ export type RelativeCommand =
80
+ | mCommand
81
+ | lCommand
82
+ | vCommand
83
+ | hCommand
84
+ | zCommand
85
+ | cCommand
86
+ | sCommand
87
+ | qCommand
88
+ | tCommand
89
+ | aCommand;
90
+
91
+ export type PathCommand = AbsoluteCommand | RelativeCommand;
92
+
93
+ export type MSegment = [MCommand, number, number];
94
+ export type mSegment = [mCommand, number, number];
95
+ export type MoveSegment = MSegment | mSegment;
96
+
97
+ export type LSegment = [LCommand, number, number];
98
+ export type lSegment = [lCommand, number, number];
99
+ export type LineSegment = LSegment | lSegment;
100
+
101
+ export type VSegment = [VCommand, number];
102
+ export type vSegment = [vCommand, number];
103
+ export type VertLineSegment = vSegment | VSegment;
104
+
105
+ export type HSegment = [HCommand, number];
106
+ export type hSegment = [hCommand, number];
107
+ export type HorLineSegment = HSegment | hSegment;
108
+
109
+ export type ZSegment = [ZCommand];
110
+ export type zSegment = [zCommand];
111
+ export type CloseSegment = ZSegment | zSegment;
112
+
113
+ export type CSegment = [CCommand, number, number, number, number, number, number];
114
+ export type cSegment = [cCommand, number, number, number, number, number, number];
115
+ export type CubicSegment = CSegment | cSegment;
116
+
117
+ export type SSegment = [SCommand, number, number, number, number];
118
+ export type sSegment = [sCommand, number, number, number, number];
119
+ export type ShortCubicSegment = SSegment | sSegment;
120
+
121
+ export type QSegment = [QCommand, number, number, number, number];
122
+ export type qSegment = [qCommand, number, number, number, number];
123
+ export type QuadSegment = QSegment | qSegment;
124
+
125
+ export type TSegment = [TCommand, number, number];
126
+ export type tSegment = [tCommand, number, number];
127
+ export type ShortQuadSegment = TSegment | tSegment;
128
+
129
+ export type ASegment = [ACommand, number, number, number, number, number, number, number];
130
+ export type aSegment = [aCommand, number, number, number, number, number, number, number];
131
+ export type ArcSegment = ASegment | aSegment;
132
+
133
+ export type PathSegment =
134
+ | MoveSegment
135
+ | LineSegment
136
+ | VertLineSegment
137
+ | HorLineSegment
138
+ | CloseSegment
139
+ | CubicSegment
140
+ | ShortCubicSegment
141
+ | QuadSegment
142
+ | ShortQuadSegment
143
+ | ArcSegment;
144
+
145
+ export type ShortSegment = VertLineSegment | HorLineSegment | ShortCubicSegment | ShortQuadSegment | CloseSegment;
146
+
147
+ export type AbsoluteSegment =
148
+ | MSegment
149
+ | LSegment
150
+ | VSegment
151
+ | HSegment
152
+ | CSegment
153
+ | SSegment
154
+ | QSegment
155
+ | TSegment
156
+ | ASegment
157
+ | ZSegment;
158
+
159
+ export type RelativeSegment =
160
+ | mSegment
161
+ | lSegment
162
+ | vSegment
163
+ | hSegment
164
+ | cSegment
165
+ | sSegment
166
+ | qSegment
167
+ | tSegment
168
+ | aSegment
169
+ | zSegment;
170
+
171
+ export type NormalSegment = MSegment | LSegment | CSegment | QSegment | ASegment | ZSegment;
172
+
173
+ export type PathArray = [MSegment | mSegment, ...PathSegment[]];
174
+ export type AbsoluteArray = [MSegment, ...AbsoluteSegment[]];
175
+ export type RelativeArray = [MSegment, ...RelativeSegment[]];
176
+ export type NormalArray = [MSegment, ...NormalSegment[]];
177
+ export type CurveArray = [MSegment, ...CSegment[]];
178
+ export type PolygonArray = [MSegment, ...LSegment[], ZSegment];
179
+ export type PolylineArray = [MSegment, ...LSegment[]];
180
+
181
+ export type ShapeTypes =
182
+ | SVGPolylineElement
183
+ | SVGPolygonElement
184
+ | SVGLineElement
185
+ | SVGEllipseElement
186
+ | SVGCircleElement
187
+ | SVGRectElement;
188
+
189
+ export type ShapeTags = 'line' | 'polyline' | 'polygon' | 'ellipse' | 'circle' | 'rect' | 'glyph';
190
+
191
+ export type ShapeOps = LineAttr | PolyAttr | PolyAttr | EllipseAttr | CircleAttr | RectAttr | GlyphAttr;
192
+
193
+ export type TransformObjectValues = Partial<TransformObject> & { origin: [number, number, number] };
@@ -0,0 +1,15 @@
1
+ import type { PathArray } from '../types';
2
+ import getPropertiesAtPoint from './getPropertiesAtPoint';
3
+
4
+ /**
5
+ * Returns the point in path closest to a given point.
6
+ *
7
+ * @param pathInput target `pathArray`
8
+ * @param point the given point
9
+ * @returns the best match
10
+ */
11
+ const getClosestPoint = (pathInput: string | PathArray, point: { x: number; y: number }): { x: number; y: number } => {
12
+ return getPropertiesAtPoint(pathInput, point).closest;
13
+ };
14
+
15
+ export default getClosestPoint;
@@ -1,13 +1,16 @@
1
1
  import getPathArea from './getPathArea';
2
2
  import pathToCurve from '../convert/pathToCurve';
3
+ import type { PathArray } from '../types';
3
4
 
4
5
  /**
5
6
  * Check if a path is drawn clockwise and returns true if so,
6
7
  * false otherwise.
7
8
  *
8
- * @param {SVGPath.pathArray} path the path string or `pathArray`
9
- * @returns {boolean} true when clockwise or false if not
9
+ * @param path the path string or `pathArray`
10
+ * @returns true when clockwise or false if not
10
11
  */
11
- export default function getDrawDirection(path) {
12
+ const getDrawDirection = (path: string | PathArray) => {
12
13
  return getPathArea(pathToCurve(path)) >= 0;
13
- }
14
+ };
15
+
16
+ export default getDrawDirection;
@@ -0,0 +1,70 @@
1
+ import pathToCurve from '../convert/pathToCurve';
2
+ import type { PathArray } from '../types';
3
+
4
+ /**
5
+ * Returns the area of a single cubic-bezier segment.
6
+ *
7
+ * http://objectmix.com/graphics/133553-area-closed-bezier-curve.html
8
+ *
9
+ * @param x1 the starting point X
10
+ * @param y1 the starting point Y
11
+ * @param c1x the first control point X
12
+ * @param c1y the first control point Y
13
+ * @param c2x the second control point X
14
+ * @param c2y the second control point Y
15
+ * @param x2 the ending point X
16
+ * @param y2 the ending point Y
17
+ * @returns the area of the cubic-bezier segment
18
+ */
19
+ const getCubicSegArea = (
20
+ x1: number,
21
+ y1: number,
22
+ c1x: number,
23
+ c1y: number,
24
+ c2x: number,
25
+ c2y: number,
26
+ x2: number,
27
+ y2: number,
28
+ ) => {
29
+ return (
30
+ (3 *
31
+ ((y2 - y1) * (c1x + c2x) -
32
+ (x2 - x1) * (c1y + c2y) +
33
+ c1y * (x1 - c2x) -
34
+ c1x * (y1 - c2y) +
35
+ y2 * (c2x + x1 / 3) -
36
+ x2 * (c2y + y1 / 3))) /
37
+ 20
38
+ );
39
+ };
40
+
41
+ /**
42
+ * Returns the area of a shape.
43
+ *
44
+ * @author Jürg Lehni & Jonathan Puckey
45
+ *
46
+ * @see https://github.com/paperjs/paper.js/blob/develop/src/path/Path.js
47
+ *
48
+ * @param path the shape `pathArray`
49
+ * @returns the length of the cubic-bezier segment
50
+ */
51
+ const getPathArea = (path: PathArray) => {
52
+ let x = 0;
53
+ let y = 0;
54
+ let len = 0;
55
+
56
+ return pathToCurve(path)
57
+ .map(seg => {
58
+ switch (seg[0]) {
59
+ case 'M':
60
+ [, x, y] = seg;
61
+ return 0;
62
+ default:
63
+ len = getCubicSegArea(x, y, ...(seg.slice(1) as [number, number, number, number, number, number]));
64
+ [x, y] = seg.slice(-2) as [number, number];
65
+ return len;
66
+ }
67
+ })
68
+ .reduce((a, b) => a + b, 0);
69
+ };
70
+ export default getPathArea;
@@ -1,15 +1,25 @@
1
+ import { PathBBox } from 'src/interface';
2
+ import { PathArray } from 'src/types';
1
3
  import pathLengthFactory from './pathLengthFactory';
2
4
 
3
5
  /**
4
6
  * Returns the bounding box of a shape.
5
7
  *
6
- * @param {SVGPath.pathArray=} path the shape `pathArray`
7
- * @returns {SVGPath.pathBBox} the length of the cubic-bezier segment
8
+ * @param path the shape `pathArray`
9
+ * @returns the length of the cubic-bezier segment
8
10
  */
9
- export default function getPathBBox(path) {
11
+ const getPathBBox = (path?: PathArray | string): PathBBox => {
10
12
  if (!path) {
11
13
  return {
12
- x: 0, y: 0, width: 0, height: 0, x2: 0, y2: 0, cx: 0, cy: 0, cz: 0,
14
+ x: 0,
15
+ y: 0,
16
+ width: 0,
17
+ height: 0,
18
+ x2: 0,
19
+ y2: 0,
20
+ cx: 0,
21
+ cy: 0,
22
+ cz: 0,
13
23
  };
14
24
  }
15
25
 
@@ -33,4 +43,5 @@ export default function getPathBBox(path) {
33
43
  // an estimted guess
34
44
  cz: Math.max(width, height) + Math.min(width, height) / 2,
35
45
  };
36
- }
46
+ };
47
+ export default getPathBBox;