svg-path-commander 2.1.0 → 2.1.2

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 (63) hide show
  1. package/README.md +63 -7
  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 +314 -40
  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 +1085 -1032
  8. package/dist/svg-path-commander.mjs.map +1 -1
  9. package/package.json +8 -8
  10. package/src/convert/pathToAbsolute.ts +5 -88
  11. package/src/convert/pathToCurve.ts +22 -28
  12. package/src/convert/pathToRelative.ts +4 -78
  13. package/src/convert/pathToString.ts +40 -7
  14. package/src/index.ts +145 -58
  15. package/src/interface.ts +3 -2
  16. package/src/math/arcTools.ts +259 -80
  17. package/src/math/bezier.ts +58 -58
  18. package/src/math/cubicTools.ts +68 -25
  19. package/src/math/distanceSquareRoot.ts +3 -1
  20. package/src/math/lineTools.ts +42 -25
  21. package/src/math/midPoint.ts +3 -1
  22. package/src/math/polygonTools.ts +48 -0
  23. package/src/math/quadTools.ts +46 -25
  24. package/src/math/rotateVector.ts +3 -2
  25. package/src/math/roundTo.ts +7 -0
  26. package/src/parser/finalizeSegment.ts +11 -7
  27. package/src/parser/parsePathString.ts +4 -5
  28. package/src/parser/pathParser.ts +1 -1
  29. package/src/process/absolutizeSegment.ts +63 -0
  30. package/src/process/arcToCubic.ts +2 -2
  31. package/src/process/iterate.ts +58 -0
  32. package/src/process/lineToCubic.ts +1 -1
  33. package/src/process/normalizePath.ts +17 -28
  34. package/src/process/normalizeSegment.ts +55 -18
  35. package/src/process/optimizePath.ts +40 -60
  36. package/src/process/projection2d.ts +4 -3
  37. package/src/process/relativizeSegment.ts +59 -0
  38. package/src/process/reverseCurve.ts +8 -5
  39. package/src/process/reversePath.ts +88 -75
  40. package/src/process/roundPath.ts +18 -14
  41. package/src/process/roundSegment.ts +9 -0
  42. package/src/process/segmentToCubic.ts +10 -8
  43. package/src/process/shortenSegment.ts +26 -34
  44. package/src/process/splitCubic.ts +10 -9
  45. package/src/process/splitPath.ts +38 -4
  46. package/src/process/transformPath.ts +75 -73
  47. package/src/types.ts +30 -0
  48. package/src/util/getPathArea.ts +3 -3
  49. package/src/util/getPathBBox.ts +69 -19
  50. package/src/util/getPointAtLength.ts +100 -4
  51. package/src/util/getPropertiesAtLength.ts +3 -4
  52. package/src/util/getPropertiesAtPoint.ts +5 -5
  53. package/src/util/getTotalLength.ts +56 -4
  54. package/test/class.test.ts +17 -14
  55. package/test/fixtures/shapes.js +26 -26
  56. package/test/fixtures/simpleShapes.js +18 -18
  57. package/test/static.test.ts +54 -28
  58. package/cypress.config.ts +0 -29
  59. package/src/math/polygonArea.ts +0 -27
  60. package/src/math/polygonLength.ts +0 -21
  61. package/src/process/fixArc.ts +0 -23
  62. package/src/process/replaceArc.ts +0 -52
  63. package/src/util/pathFactory.ts +0 -130
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "svg-path-commander",
3
3
  "author": "thednp",
4
4
  "license": "MIT",
5
- "version": "2.1.0",
5
+ "version": "2.1.2",
6
6
  "description": "Modern TypeScript tools for SVG",
7
7
  "source": "./src/index.ts",
8
8
  "main": "./dist/svg-path-commander.js",
@@ -40,19 +40,19 @@
40
40
  "devDependencies": {
41
41
  "@typescript-eslint/eslint-plugin": "^5.62.0",
42
42
  "@typescript-eslint/parser": "^5.62.0",
43
- "@vitest/browser": "^2.1.2",
44
- "@vitest/coverage-istanbul": "^2.1.2",
45
- "@vitest/ui": "^2.1.2",
43
+ "@vitest/browser": "^2.1.3",
44
+ "@vitest/coverage-istanbul": "^2.1.3",
45
+ "@vitest/ui": "^2.1.3",
46
46
  "dts-bundle-generator": "^9.5.1",
47
47
  "eslint": "^8.57.1",
48
48
  "eslint-plugin-jsdoc": "^46.10.1",
49
49
  "eslint-plugin-prefer-arrow": "^1.2.3",
50
50
  "eslint-plugin-prettier": "^4.2.1",
51
- "playwright": "^1.47.2",
51
+ "playwright": "^1.48.1",
52
52
  "prettier": "^2.8.8",
53
- "typescript": "^5.6.2",
54
- "vite": "^5.4.8",
55
- "vitest": "^2.1.2"
53
+ "typescript": "^5.6.3",
54
+ "vite": "^5.4.9",
55
+ "vitest": "^2.1.3"
56
56
  },
57
57
  "dependencies": {
58
58
  "@thednp/dommatrix": "^2.0.8"
@@ -1,19 +1,7 @@
1
1
  import parsePathString from '../parser/parsePathString';
2
- import isAbsoluteArray from '../util/isAbsoluteArray';
3
- import type {
4
- PathArray,
5
- AbsoluteArray,
6
- AbsoluteCommand,
7
- AbsoluteSegment,
8
- VSegment,
9
- HSegment,
10
- QSegment,
11
- TSegment,
12
- ASegment,
13
- SSegment,
14
- CSegment,
15
- MSegment,
16
- } from '../types';
2
+ import absolutizeSegment from '../process/absolutizeSegment';
3
+ import type { AbsoluteArray, PathArray } from '../types';
4
+ import iterate from '../process/iterate';
17
5
 
18
6
  /**
19
7
  * Parses a path string value or object and returns an array
@@ -22,80 +10,9 @@ import type {
22
10
  * @param pathInput the path string | object
23
11
  * @returns the resulted `pathArray` with absolute values
24
12
  */
25
- const pathToAbsolute = (pathInput: string | PathArray): AbsoluteArray => {
26
- /* istanbul ignore else */
27
- if (isAbsoluteArray(pathInput)) {
28
- return pathInput.slice(0) as AbsoluteArray;
29
- }
30
-
13
+ const pathToAbsolute = (pathInput: string | PathArray) => {
31
14
  const path = parsePathString(pathInput);
32
- let x = 0;
33
- let y = 0;
34
- let mx = 0;
35
- let my = 0;
36
-
37
- // the `absoluteSegment[]` is for sure an `absolutePath`
38
- return path.map(segment => {
39
- const values = segment.slice(1).map(Number);
40
- const [pathCommand] = segment;
41
- const absCommand = pathCommand.toUpperCase() as AbsoluteCommand;
42
-
43
- if (pathCommand === 'M') {
44
- [x, y] = values;
45
- mx = x;
46
- my = y;
47
- return ['M', x, y] as MSegment;
48
- }
49
-
50
- let absoluteSegment = [] as unknown as AbsoluteSegment;
51
-
52
- if (pathCommand !== absCommand) {
53
- if (absCommand === 'A') {
54
- absoluteSegment = [
55
- absCommand,
56
- values[0],
57
- values[1],
58
- values[2],
59
- values[3],
60
- values[4],
61
- values[5] + x,
62
- values[6] + y,
63
- ] as ASegment;
64
- } else if (absCommand === 'V') {
65
- absoluteSegment = [absCommand, values[0] + y] as VSegment;
66
- } else if (absCommand === 'H') {
67
- absoluteSegment = [absCommand, values[0] + x] as HSegment;
68
- } else {
69
- // use brakets for `eslint: no-case-declaration`
70
- // https://stackoverflow.com/a/50753272/803358
71
- const absValues = values.map((n, j) => n + (j % 2 ? y : x));
72
- // for n, l, c, s, q, t
73
- absoluteSegment = [absCommand, ...absValues] as QSegment | TSegment | SSegment | CSegment;
74
- }
75
- } else {
76
- absoluteSegment = [absCommand, ...values] as typeof segment;
77
- }
78
-
79
- // const segLength = absoluteSegment.length;
80
- if (absCommand === 'Z') {
81
- x = mx;
82
- y = my;
83
- } else if (absCommand === 'H') {
84
- [, x] = absoluteSegment as HSegment;
85
- } else if (absCommand === 'V') {
86
- [, y] = absoluteSegment as VSegment;
87
- } else {
88
- // x = absoluteSegment[segLength - 2];
89
- // y = absoluteSegment[segLength - 1];
90
- [x, y] = absoluteSegment.slice(-2) as [number, number];
91
-
92
- if (absCommand === 'M') {
93
- mx = x;
94
- my = y;
95
- }
96
- }
97
15
 
98
- return absoluteSegment;
99
- }) as AbsoluteArray;
16
+ return iterate<AbsoluteArray>(path, absolutizeSegment);
100
17
  };
101
18
  export default pathToAbsolute;
@@ -1,9 +1,9 @@
1
- import fixArc from '../process/fixArc';
2
- import isCurveArray from '../util/isCurveArray';
3
- import normalizePath from '../process/normalizePath';
4
1
  import segmentToCubic from '../process/segmentToCubic';
2
+ import { AbsoluteCommand, CSegment, CurveArray, PathArray } from '../types';
3
+ import iterate from '../process/iterate';
4
+ import parsePathString from '../parser/parsePathString';
5
+ import normalizeSegment from '../process/normalizeSegment';
5
6
  import paramsParser from '../parser/paramsParser';
6
- import { CurveArray, PathArray, PathCommand } from '../types';
7
7
 
8
8
  /**
9
9
  * Parses a path string value or 'pathArray' and returns a new one
@@ -16,34 +16,28 @@ import { CurveArray, PathArray, PathCommand } from '../types';
16
16
  * @returns the resulted `pathArray` converted to cubic-bezier
17
17
  */
18
18
  const pathToCurve = (pathInput: string | PathArray): CurveArray => {
19
- /* istanbul ignore else */
20
- if (isCurveArray(pathInput)) {
21
- return pathInput.slice(0) as CurveArray;
22
- }
23
-
24
- const path = normalizePath(pathInput);
25
19
  const params = { ...paramsParser };
26
- const allPathCommands = [] as PathCommand[];
27
- let pathCommand = ''; // ts-lint
28
- let ii = path.length;
29
-
30
- for (let i = 0; i < ii; i += 1) {
31
- [pathCommand] = path[i];
32
- allPathCommands[i] = pathCommand as PathCommand;
20
+ const path = parsePathString(pathInput);
33
21
 
34
- path[i] = segmentToCubic(path[i], params);
22
+ return iterate<CurveArray>(path, (seg, index, lastX, lastY) => {
23
+ params.x = lastX;
24
+ params.y = lastY;
25
+ const normalSegment = normalizeSegment(seg, params);
26
+ let result = segmentToCubic(normalSegment, params);
27
+ const isLongArc = result[0] === 'C' && result.length > 7;
35
28
 
36
- fixArc(path, allPathCommands, i);
37
- ii = path.length;
29
+ if (isLongArc) {
30
+ path.splice(index + 1, 0, ['C' as AbsoluteCommand | number].concat(result.slice(7)) as CSegment);
31
+ result = result.slice(0, 7) as CSegment;
32
+ }
38
33
 
39
- const segment = path[i];
40
- const seglen = segment.length;
41
- params.x1 = +segment[seglen - 2];
42
- params.y1 = +segment[seglen - 1];
43
- params.x2 = +segment[seglen - 4] || params.x1;
44
- params.y2 = +segment[seglen - 3] || params.y1;
45
- }
34
+ const seglen = result.length;
35
+ params.x1 = +result[seglen - 2];
36
+ params.y1 = +result[seglen - 1];
37
+ params.x2 = +result[seglen - 4] || params.x1;
38
+ params.y2 = +result[seglen - 3] || params.y1;
46
39
 
47
- return path as CurveArray;
40
+ return result;
41
+ });
48
42
  };
49
43
  export default pathToCurve;
@@ -1,14 +1,7 @@
1
- import type {
2
- aSegment,
3
- hSegment,
4
- PathArray,
5
- RelativeArray,
6
- RelativeCommand,
7
- RelativeSegment,
8
- vSegment,
9
- } from '../types';
1
+ import type { PathArray, RelativeArray } from '../types';
10
2
  import parsePathString from '../parser/parsePathString';
11
- import isRelativeArray from '../util/isRelativeArray';
3
+ import iterate from '../process/iterate';
4
+ import relativizeSegment from '../process/relativizeSegment';
12
5
 
13
6
  /**
14
7
  * Parses a path string value or object and returns an array
@@ -18,75 +11,8 @@ import isRelativeArray from '../util/isRelativeArray';
18
11
  * @returns the resulted `pathArray` with relative values
19
12
  */
20
13
  const pathToRelative = (pathInput: string | PathArray): RelativeArray => {
21
- /* istanbul ignore else */
22
- if (isRelativeArray(pathInput)) {
23
- return pathInput.slice(0) as RelativeArray;
24
- }
25
-
26
14
  const path = parsePathString(pathInput);
27
- let x = 0;
28
- let y = 0;
29
- let mx = 0;
30
- let my = 0;
31
-
32
- return path.map(segment => {
33
- const values = segment.slice(1).map(Number);
34
- const [pathCommand] = segment;
35
- const relativeCommand = pathCommand.toLowerCase() as RelativeCommand;
36
-
37
- if (pathCommand === 'M') {
38
- [x, y] = values;
39
- mx = x;
40
- my = y;
41
- return ['M', x, y];
42
- }
43
-
44
- let relativeSegment = [];
45
-
46
- if (pathCommand !== relativeCommand) {
47
- if (relativeCommand === 'a') {
48
- relativeSegment = [
49
- relativeCommand,
50
- values[0],
51
- values[1],
52
- values[2],
53
- values[3],
54
- values[4],
55
- values[5] - x,
56
- values[6] - y,
57
- ] as aSegment;
58
- } else if (relativeCommand === 'v') {
59
- relativeSegment = [relativeCommand, values[0] - y] as vSegment;
60
- } else if (relativeCommand === 'h') {
61
- relativeSegment = [relativeCommand, values[0] - x] as hSegment;
62
- } else {
63
- // use brakets for `eslint: no-case-declaration`
64
- // https://stackoverflow.com/a/50753272/803358
65
- const relValues = values.map((n, j) => n - (j % 2 ? y : x));
66
- relativeSegment = [relativeCommand, ...relValues] as RelativeSegment;
67
- }
68
- } else {
69
- if (pathCommand === 'm') {
70
- mx = values[0] + x;
71
- my = values[1] + y;
72
- }
73
- relativeSegment = [relativeCommand, ...values] as RelativeSegment;
74
- }
75
-
76
- const segLength = relativeSegment.length;
77
- if (relativeCommand === 'z') {
78
- x = mx;
79
- y = my;
80
- } else if (relativeCommand === 'h') {
81
- x += relativeSegment[1] as number;
82
- } else if (relativeCommand === 'v') {
83
- y += relativeSegment[1] as number;
84
- } else {
85
- x += relativeSegment[segLength - 2] as number;
86
- y += relativeSegment[segLength - 1] as number;
87
- }
88
15
 
89
- return relativeSegment as typeof segment;
90
- }) as RelativeArray;
16
+ return iterate<RelativeArray>(path, relativizeSegment);
91
17
  };
92
18
  export default pathToRelative;
@@ -1,17 +1,50 @@
1
- import type { PathArray } from '../types';
2
- import roundPath from '../process/roundPath';
1
+ import type { PathArray, PathSegment } from '../types';
2
+ import defaultOptions from '../options/options';
3
+ import roundTo from '../math/roundTo';
3
4
 
4
5
  /**
5
6
  * Returns a valid `d` attribute string value created
6
7
  * by rounding values and concatenating the `pathArray` segments.
7
8
  *
8
9
  * @param path the `pathArray` object
9
- * @param round amount of decimals to round values to
10
+ * @param roundOption amount of decimals to round values to
10
11
  * @returns the concatenated path string
11
12
  */
12
- const pathToString = (path: PathArray, round?: number | 'off'): string => {
13
- return roundPath(path, round)
14
- .map(x => x[0] + x.slice(1).join(' '))
15
- .join('');
13
+ const pathToString = (path: PathArray, roundOption?: number | 'off'): string => {
14
+ const pathLen = path.length;
15
+ let { round } = defaultOptions;
16
+ let segment = path[0] as PathSegment;
17
+ let result = '';
18
+
19
+ // allow for ZERO decimals
20
+ round =
21
+ roundOption === 'off'
22
+ ? roundOption
23
+ : typeof roundOption === 'number' && roundOption >= 0
24
+ ? roundOption
25
+ : typeof round === 'number' && round >= 0
26
+ ? round
27
+ : /* istanbul ignore next @preserve */ 'off';
28
+
29
+ for (let i = 0; i < pathLen; i += 1) {
30
+ segment = path[i];
31
+ const [pathCommand] = segment;
32
+ const values = segment.slice(1) as number[];
33
+ result += pathCommand;
34
+ if (round === 'off') {
35
+ result += values.join(' ');
36
+ } else {
37
+ let j = 0;
38
+ const valLen = values.length;
39
+ while (j < valLen) {
40
+ result += roundTo(values[j], round);
41
+ if (j !== valLen - 1) result += ' ';
42
+ j += 1;
43
+ }
44
+ }
45
+ }
46
+
47
+ return result;
16
48
  };
49
+
17
50
  export default pathToString;
package/src/index.ts CHANGED
@@ -1,54 +1,99 @@
1
- import { PathArray, TransformObjectValues } from './types';
1
+ 'use strict';
2
+ import CSSMatrix from '@thednp/dommatrix';
3
+ import { PointTuple, PathArray, TransformObjectValues } from './types';
2
4
  import type { Options, TransformEntries, TransformObject } from './interface';
3
5
  export * from './types';
4
6
  export * from './interface';
5
7
  import defaultOptions from './options/options';
6
8
 
9
+ import pathToAbsolute from './convert/pathToAbsolute';
10
+ import pathToRelative from './convert/pathToRelative';
11
+ import pathToCurve from './convert/pathToCurve';
12
+ import pathToString from './convert/pathToString';
13
+ import * as arcTools from './math/arcTools';
14
+ import {
15
+ Cvalues,
16
+ Tvalues,
17
+ minmaxC,
18
+ minmaxQ,
19
+ getBezierLength,
20
+ bezierLength,
21
+ calculateBezier,
22
+ computeBezier,
23
+ deriveBezier,
24
+ CBEZIER_MINMAX_EPSILON,
25
+ } from './math/bezier';
26
+ import { getCubicLength, getCubicBBox, getPointAtCubicLength, getPointAtCubicSegmentLength } from './math/cubicTools';
27
+ import { getPointAtLineLength, getLineBBox, getLineLength } from './math/lineTools';
28
+ import { getPointAtQuadSegmentLength, getQuadLength, getQuadBBox, getPointAtQuadLength } from './math/quadTools';
29
+ import { polygonArea, polygonLength } from './math/polygonTools';
30
+
31
+ import distanceSquareRoot from './math/distanceSquareRoot';
32
+ import midPoint from './math/midPoint';
33
+ import rotateVector from './math/rotateVector';
34
+ import roundTo from './math/roundTo';
35
+
7
36
  import error from './parser/error';
8
37
  import parsePathString from './parser/parsePathString';
9
-
10
- import polygonArea from './math/polygonArea';
11
- import polygonLength from './math/polygonLength';
12
-
13
- import CSSMatrix from '@thednp/dommatrix';
14
- import getPathBBox from './util/getPathBBox';
15
- import getPathArea from './util/getPathArea';
16
- import getTotalLength from './util/getTotalLength';
38
+ import finalizeSegment from './parser/finalizeSegment';
39
+ import invalidPathValue from './parser/invalidPathValue';
40
+ import isArcCommand from './parser/isArcCommand';
41
+ import isDigit from './parser/isDigit';
42
+ import isDigitStart from './parser/isDigitStart';
43
+ import isMoveCommand from './parser/isMoveCommand';
44
+ import isPathCommand from './parser/isPathCommand';
45
+ import isSpace from './parser/isSpace';
46
+ import paramsCount from './parser/paramsCount';
47
+ import paramsParser from './parser/paramsParser';
48
+ import pathParser from './parser/pathParser';
49
+ import scanFlag from './parser/scanFlag';
50
+ import scanParam from './parser/scanParam';
51
+ import scanSegment from './parser/scanSegment';
52
+ import skipSpaces from './parser/skipSpaces';
53
+
54
+ import distanceEpsilon from './util/distanceEpsilon';
55
+ import getClosestPoint from './util/getClosestPoint';
17
56
  import getDrawDirection from './util/getDrawDirection';
57
+ import getPathArea from './util/getPathArea';
58
+ import getPathBBox from './util/getPathBBox';
18
59
  import getPointAtLength from './util/getPointAtLength';
19
- import pathFactory from './util/pathFactory';
20
-
21
60
  import getPropertiesAtLength from './util/getPropertiesAtLength';
22
61
  import getPropertiesAtPoint from './util/getPropertiesAtPoint';
23
- import getClosestPoint from './util/getClosestPoint';
24
- import getSegmentOfPoint from './util/getSegmentOfPoint';
25
62
  import getSegmentAtLength from './util/getSegmentAtLength';
26
- import isPointInStroke from './util/isPointInStroke';
63
+ import getSegmentOfPoint from './util/getSegmentOfPoint';
64
+ import getTotalLength from './util/getTotalLength';
27
65
 
28
- import isValidPath from './util/isValidPath';
29
- import isPathArray from './util/isPathArray';
30
66
  import isAbsoluteArray from './util/isAbsoluteArray';
31
- import isRelativeArray from './util/isRelativeArray';
32
67
  import isCurveArray from './util/isCurveArray';
33
68
  import isNormalizedArray from './util/isNormalizedArray';
34
- import shapeToPathArray from './util/shapeToPathArray';
69
+ import isPathArray from './util/isPathArray';
70
+ import isPointInStroke from './util/isPointInStroke';
71
+ import isRelativeArray from './util/isRelativeArray';
72
+ import isValidPath from './util/isValidPath';
73
+ import shapeParams from './util/shapeParams';
35
74
  import shapeToPath from './util/shapeToPath';
75
+ import shapeToPathArray from './util/shapeToPathArray';
36
76
 
37
- import roundPath from './process/roundPath';
38
- import splitPath from './process/splitPath';
77
+ import absolutizeSegment from './process/absolutizeSegment';
78
+ import arcToCubic from './process/arcToCubic';
39
79
  import getSVGMatrix from './process/getSVGMatrix';
80
+ import iterate from './process/iterate';
81
+ import lineToCubic from './process/lineToCubic';
82
+ import normalizePath from './process/normalizePath';
83
+ import normalizeSegment from './process/normalizeSegment';
40
84
  import optimizePath from './process/optimizePath';
85
+ import projection2d from './process/projection2d';
86
+ import quadToCubic from './process/quadToCubic';
87
+ import relativizeSegment from './process/relativizeSegment';
41
88
  import reverseCurve from './process/reverseCurve';
42
89
  import reversePath from './process/reversePath';
43
- import normalizePath from './process/normalizePath';
44
- import transformPath from './process/transformPath';
90
+ import roundPath from './process/roundPath';
91
+ import roundSegment from './process/roundSegment';
92
+ import segmentToCubic from './process/segmentToCubic';
93
+ import shortenSegment from './process/shortenSegment';
45
94
  import splitCubic from './process/splitCubic';
46
- import replaceArc from './process/replaceArc';
47
-
48
- import pathToAbsolute from './convert/pathToAbsolute';
49
- import pathToRelative from './convert/pathToRelative';
50
- import pathToCurve from './convert/pathToCurve';
51
- import pathToString from './convert/pathToString';
95
+ import splitPath from './process/splitPath';
96
+ import transformPath from './process/transformPath';
52
97
 
53
98
  /**
54
99
  * Creates a new SVGPathCommander instance with the following properties:
@@ -61,19 +106,55 @@ import pathToString from './convert/pathToString';
61
106
  * @returns a new SVGPathCommander instance
62
107
  */
63
108
  class SVGPathCommander {
64
- // bring main utilities to front
65
109
  public static CSSMatrix = CSSMatrix;
66
- public static getSVGMatrix = getSVGMatrix;
110
+ public static pathToAbsolute = pathToAbsolute;
111
+ public static pathToRelative = pathToRelative;
112
+ public static pathToCurve = pathToCurve;
113
+ public static pathToString = pathToString;
114
+ public static arcTools = arcTools;
115
+ public static bezierTools = {
116
+ Cvalues,
117
+ Tvalues,
118
+ minmaxC,
119
+ minmaxQ,
120
+ getBezierLength,
121
+ bezierLength,
122
+ calculateBezier,
123
+ computeBezier,
124
+ deriveBezier,
125
+ CBEZIER_MINMAX_EPSILON,
126
+ };
127
+ public static cubicTools = { getCubicLength, getCubicBBox, getPointAtCubicLength, getPointAtCubicSegmentLength };
128
+ public static lineTools = { getPointAtLineLength, getLineBBox, getLineLength };
129
+ public static quadTools = { getPointAtQuadSegmentLength, getQuadLength, getQuadBBox, getPointAtQuadLength };
130
+ public static polygonTools = { polygonArea, polygonLength };
131
+ public static distanceSquareRoot = distanceSquareRoot;
132
+ public static distanceEpsilon = distanceEpsilon;
133
+ public static midPoint = midPoint;
134
+ public static rotateVector = rotateVector;
135
+ public static roundTo = roundTo;
136
+ public static finalizeSegment = finalizeSegment;
137
+ public static invalidPathValue = invalidPathValue;
138
+ public static isArcCommand = isArcCommand;
139
+ public static isDigit = isDigit;
140
+ public static isDigitStart = isDigitStart;
141
+ public static isMoveCommand = isMoveCommand;
142
+ public static isPathCommand = isPathCommand;
143
+ public static isSpace = isSpace;
144
+ public static paramsCount = paramsCount;
145
+ public static paramsParser = paramsParser;
146
+ public static pathParser = pathParser;
147
+ public static scanFlag = scanFlag;
148
+ public static scanParam = scanParam;
149
+ public static scanSegment = scanSegment;
150
+ public static skipSpaces = skipSpaces;
67
151
  public static getPathBBox = getPathBBox;
68
152
  public static getPathArea = getPathArea;
69
153
  public static getTotalLength = getTotalLength;
70
154
  public static getDrawDirection = getDrawDirection;
71
155
  public static getPointAtLength = getPointAtLength;
72
- public static pathFactory = pathFactory;
73
156
  public static getPropertiesAtLength = getPropertiesAtLength;
74
157
  public static getPropertiesAtPoint = getPropertiesAtPoint;
75
- public static polygonLength = polygonLength;
76
- public static polygonArea = polygonArea;
77
158
  public static getClosestPoint = getClosestPoint;
78
159
  public static getSegmentOfPoint = getSegmentOfPoint;
79
160
  public static getSegmentAtLength = getSegmentAtLength;
@@ -86,20 +167,28 @@ class SVGPathCommander {
86
167
  public static isNormalizedArray = isNormalizedArray;
87
168
  public static shapeToPath = shapeToPath;
88
169
  public static shapeToPathArray = shapeToPathArray;
170
+ public static shapeParams = shapeParams;
89
171
  public static parsePathString = parsePathString;
90
- public static roundPath = roundPath;
91
- public static splitPath = splitPath;
92
- public static splitCubic = splitCubic;
93
- public static replaceArc = replaceArc;
172
+ public static absolutizeSegment = absolutizeSegment;
173
+ public static arcToCubic = arcToCubic;
174
+ public static getSVGMatrix = getSVGMatrix;
175
+ public static iterate = iterate;
176
+ public static lineToCubic = lineToCubic;
177
+ public static normalizePath = normalizePath;
178
+ public static normalizeSegment = normalizeSegment;
94
179
  public static optimizePath = optimizePath;
180
+ public static projection2d = projection2d;
181
+ public static quadToCubic = quadToCubic;
182
+ public static relativizeSegment = relativizeSegment;
95
183
  public static reverseCurve = reverseCurve;
96
184
  public static reversePath = reversePath;
97
- public static normalizePath = normalizePath;
185
+ public static roundPath = roundPath;
186
+ public static roundSegment = roundSegment;
187
+ public static segmentToCubic = segmentToCubic;
188
+ public static shortenSegment = shortenSegment;
189
+ public static splitCubic = splitCubic;
190
+ public static splitPath = splitPath;
98
191
  public static transformPath = transformPath;
99
- public static pathToAbsolute = pathToAbsolute;
100
- public static pathToRelative = pathToRelative;
101
- public static pathToCurve = pathToCurve;
102
- public static pathToString = pathToString;
103
192
  // declare class properties
104
193
  declare segments: PathArray;
105
194
  declare round: number | 'off';
@@ -118,18 +207,13 @@ class SVGPathCommander {
118
207
  throw TypeError(`${error}: "pathValue" is ${undefPath ? 'undefined' : 'empty'}`);
119
208
  }
120
209
 
121
- const segments = parsePathString(pathValue);
122
- this.segments = segments;
123
- const { width, height, cx, cy, cz } = this.bbox;
210
+ this.segments = parsePathString(pathValue);
124
211
 
125
- // set instance options.round
212
+ // // set instance options.round
126
213
  const { round: roundOption, origin: originOption } = instanceOptions;
127
214
  let round: number | 'off';
128
215
 
129
- if (roundOption === 'auto') {
130
- const pathScale = `${Math.floor(Math.max(width, height))}`.length;
131
- round = pathScale >= 4 ? 0 : 4 - pathScale;
132
- } else if (Number.isInteger(roundOption) || roundOption === 'off') {
216
+ if (Number.isInteger(roundOption) || roundOption === 'off') {
133
217
  round = roundOption as number | 'off';
134
218
  } else {
135
219
  round = defaultOptions.round as number;
@@ -137,14 +221,14 @@ class SVGPathCommander {
137
221
 
138
222
  // set instance options.origin
139
223
  // the SVGPathCommander class will always override the default origin
140
- let origin = [cx, cy, cz] as [number, number, number];
224
+ let origin = defaultOptions.origin as [number, number, number];
141
225
  /* istanbul ignore else @preserve */
142
226
  if (Array.isArray(originOption) && originOption.length >= 2) {
143
227
  const [originX, originY, originZ] = originOption.map(Number);
144
228
  origin = [
145
- !Number.isNaN(originX) ? originX : cx,
146
- !Number.isNaN(originY) ? originY : cy,
147
- !Number.isNaN(originZ) ? originZ : cz,
229
+ !Number.isNaN(originX) ? originX : 0,
230
+ !Number.isNaN(originY) ? originY : 0,
231
+ !Number.isNaN(originZ) ? originZ : 0,
148
232
  ];
149
233
  }
150
234
 
@@ -282,8 +366,9 @@ class SVGPathCommander {
282
366
  */
283
367
  optimize() {
284
368
  const { segments } = this;
369
+ const round = this.round === 'off' ? 2 : this.round;
285
370
 
286
- this.segments = optimizePath(segments, this.round);
371
+ this.segments = optimizePath(segments, round);
287
372
  return this;
288
373
  }
289
374
 
@@ -311,7 +396,7 @@ class SVGPathCommander {
311
396
  for (const [k, v] of Object.entries(source) as TransformEntries) {
312
397
  // istanbul ignore else @preserve
313
398
  if (k === 'skew' && Array.isArray(v)) {
314
- transform[k] = v.map(Number) as [number, number];
399
+ transform[k] = v.map(Number) as PointTuple;
315
400
  } else if ((k === 'rotate' || k === 'translate' || k === 'origin' || k === 'scale') && Array.isArray(v)) {
316
401
  transform[k] = v.map(Number) as [number, number, number];
317
402
  } else if (k !== 'origin' && typeof Number(v) === 'number') transform[k] = Number(v);
@@ -338,7 +423,8 @@ class SVGPathCommander {
338
423
  * @public
339
424
  */
340
425
  flipX() {
341
- this.transform({ rotate: [0, 180, 0] });
426
+ const { cx, cy } = this.bbox;
427
+ this.transform({ rotate: [0, 180, 0], origin: [cx, cy, 0] });
342
428
  return this;
343
429
  }
344
430
 
@@ -348,7 +434,8 @@ class SVGPathCommander {
348
434
  * @public
349
435
  */
350
436
  flipY() {
351
- this.transform({ rotate: [180, 0, 0] });
437
+ const { cx, cy } = this.bbox;
438
+ this.transform({ rotate: [180, 0, 0], origin: [cx, cy, 0] });
352
439
  return this;
353
440
  }
354
441