svg-path-commander 2.1.1 → 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 (56) hide show
  1. package/README.md +2 -2
  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 +207 -19
  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 +1052 -969
  8. package/dist/svg-path-commander.mjs.map +1 -1
  9. package/package.json +7 -7
  10. package/src/convert/pathToAbsolute.ts +2 -31
  11. package/src/convert/pathToCurve.ts +13 -27
  12. package/src/convert/pathToRelative.ts +2 -47
  13. package/src/convert/pathToString.ts +40 -7
  14. package/src/index.ts +132 -39
  15. package/src/interface.ts +2 -1
  16. package/src/math/arcTools.ts +53 -51
  17. package/src/math/bezier.ts +41 -33
  18. package/src/math/cubicTools.ts +10 -8
  19. package/src/math/distanceSquareRoot.ts +1 -1
  20. package/src/math/lineTools.ts +6 -4
  21. package/src/math/polygonTools.ts +48 -0
  22. package/src/math/quadTools.ts +8 -6
  23. package/src/math/rotateVector.ts +3 -2
  24. package/src/math/roundTo.ts +7 -0
  25. package/src/parser/finalizeSegment.ts +11 -7
  26. package/src/parser/parsePathString.ts +2 -3
  27. package/src/parser/pathParser.ts +1 -1
  28. package/src/process/absolutizeSegment.ts +35 -30
  29. package/src/process/arcToCubic.ts +2 -2
  30. package/src/process/iterate.ts +41 -16
  31. package/src/process/lineToCubic.ts +1 -1
  32. package/src/process/normalizePath.ts +14 -31
  33. package/src/process/normalizeSegment.ts +53 -15
  34. package/src/process/optimizePath.ts +40 -60
  35. package/src/process/projection2d.ts +2 -2
  36. package/src/process/relativizeSegment.ts +33 -35
  37. package/src/process/reverseCurve.ts +8 -5
  38. package/src/process/reversePath.ts +87 -74
  39. package/src/process/roundPath.ts +14 -8
  40. package/src/process/roundSegment.ts +9 -0
  41. package/src/process/segmentToCubic.ts +9 -7
  42. package/src/process/shortenSegment.ts +24 -32
  43. package/src/process/splitCubic.ts +2 -2
  44. package/src/process/transformPath.ts +35 -40
  45. package/src/types.ts +7 -11
  46. package/src/util/getPathArea.ts +2 -2
  47. package/src/util/getPathBBox.ts +25 -42
  48. package/src/util/getPointAtLength.ts +51 -49
  49. package/src/util/getPropertiesAtLength.ts +4 -5
  50. package/src/util/getPropertiesAtPoint.ts +5 -5
  51. package/src/util/getTotalLength.ts +23 -38
  52. package/test/class.test.ts +2 -0
  53. package/test/fixtures/shapes.js +5 -5
  54. package/test/static.test.ts +18 -12
  55. package/src/math/polygonArea.ts +0 -29
  56. package/src/math/polygonLength.ts +0 -22
@@ -8,8 +8,9 @@
8
8
  * @returns the rotated vector
9
9
  */
10
10
  const rotateVector = (x: number, y: number, rad: number): { x: number; y: number } => {
11
- const X = x * Math.cos(rad) - y * Math.sin(rad);
12
- const Y = x * Math.sin(rad) + y * Math.cos(rad);
11
+ const { sin, cos } = Math;
12
+ const X = x * cos(rad) - y * sin(rad);
13
+ const Y = x * sin(rad) + y * cos(rad);
13
14
  return { x: X, y: Y };
14
15
  };
15
16
 
@@ -0,0 +1,7 @@
1
+ const roundTo = (n: number, round: number) => {
2
+ const pow = round >= 1 ? 10 ** round : 1;
3
+
4
+ return round > 0 ? Math.round(n * pow) / pow : Math.round(n);
5
+ };
6
+
7
+ export default roundTo;
@@ -9,21 +9,25 @@ import type { PathCommand, PathSegment, RelativeCommand } from '../types';
9
9
  */
10
10
  const finalizeSegment = (path: PathParser) => {
11
11
  let pathCommand = path.pathValue[path.segmentStart] as PathCommand;
12
- let LK = pathCommand.toLowerCase() as RelativeCommand;
12
+ let relativeCommand = pathCommand.toLowerCase() as RelativeCommand;
13
13
  const { data } = path;
14
14
 
15
- while (data.length >= paramsCount[LK]) {
15
+ while (data.length >= paramsCount[relativeCommand]) {
16
16
  // overloaded `moveTo`
17
17
  // https://github.com/rveciana/svg-path-properties/blob/master/src/parse.ts
18
- if (LK === 'm' && data.length > 2) {
19
- path.segments.push([pathCommand, ...(data.splice(0, 2) as number[])] as PathSegment);
20
- LK = 'l';
18
+ if (relativeCommand === 'm' && data.length > 2) {
19
+ path.segments.push([pathCommand as PathCommand | number].concat(data.splice(0, 2) as number[]) as PathSegment);
20
+ relativeCommand = 'l';
21
21
  pathCommand = pathCommand === 'm' ? 'l' : 'L';
22
22
  } else {
23
- path.segments.push([pathCommand, ...(data.splice(0, paramsCount[LK]) as number[])] as PathSegment);
23
+ path.segments.push(
24
+ [pathCommand as PathCommand | number].concat(
25
+ data.splice(0, paramsCount[relativeCommand]) as number[],
26
+ ) as PathSegment,
27
+ );
24
28
  }
25
29
 
26
- if (!paramsCount[LK]) {
30
+ if (!paramsCount[relativeCommand]) {
27
31
  break;
28
32
  }
29
33
  }
@@ -10,10 +10,9 @@ import type { PathArray } from '../types';
10
10
  * @param pathInput the string to be parsed
11
11
  * @returns the resulted `pathArray` or error string
12
12
  */
13
- const parsePathString = (pathInput: string | PathArray) => {
13
+ const parsePathString = <T extends PathArray>(pathInput: string | T) => {
14
14
  if (typeof pathInput !== 'string') {
15
- return pathInput.slice(0) as PathArray;
16
- // return pathInput;
15
+ return pathInput.slice(0) as typeof pathInput;
17
16
  }
18
17
 
19
18
  const path = new PathParser(pathInput);
@@ -13,7 +13,7 @@ export default class PathParser {
13
13
  declare index: number;
14
14
  declare param: number;
15
15
  declare segmentStart: number;
16
- declare data: any[];
16
+ declare data: (string | number)[];
17
17
  declare err: string;
18
18
 
19
19
  constructor(pathString: string) {
@@ -1,4 +1,3 @@
1
- import type { ParserParams } from '../interface';
2
1
  import type {
3
2
  AbsoluteSegment,
4
3
  AbsoluteCommand,
@@ -11,48 +10,54 @@ import type {
11
10
  CSegment,
12
11
  PathSegment,
13
12
  MSegment,
13
+ LSegment,
14
14
  } from '../types';
15
15
 
16
16
  /**
17
17
  * Returns an absolute segment of a `PathArray` object.
18
18
  *
19
19
  * @param segment the segment object
20
- * @param params the coordinates of the previous segment
20
+ * @param index the segment index
21
+ * @param lastX the last known X value
22
+ * @param lastY the last known Y value
21
23
  * @returns the absolute segment
22
24
  */
23
- const absolutizeSegment = (segment: PathSegment, params: ParserParams) => {
25
+ const absolutizeSegment = (segment: PathSegment, index: number, lastX: number, lastY: number) => {
24
26
  const [pathCommand] = segment;
25
- const { x, y } = params;
26
- const values = segment.slice(1).map(Number);
27
27
  const absCommand = pathCommand.toUpperCase() as AbsoluteCommand;
28
28
  const isAbsolute = absCommand === pathCommand;
29
29
 
30
30
  /* istanbul ignore else @preserve */
31
- if (!isAbsolute) {
32
- if (absCommand === 'A') {
33
- return [
34
- absCommand,
35
- values[0],
36
- values[1],
37
- values[2],
38
- values[3],
39
- values[4],
40
- values[5] + x,
41
- values[6] + y,
42
- ] as ASegment;
43
- } else if (absCommand === 'V') {
44
- return [absCommand, values[0] + y] as VSegment;
45
- } else if (absCommand === 'H') {
46
- return [absCommand, values[0] + x] as HSegment;
47
- } else {
48
- // use brakets for `eslint: no-case-declaration`
49
- // https://stackoverflow.com/a/50753272/803358
50
- const absValues = values.map((n, j) => n + (j % 2 ? y : x));
51
- // for n, l, c, s, q, t
52
- return [absCommand, ...absValues] as MSegment | QSegment | TSegment | SSegment | CSegment;
53
- }
31
+ if (index === 0 || isAbsolute) return segment as MSegment | AbsoluteSegment;
32
+ // const values = segment.slice(1) as number[];
33
+ if (absCommand === 'A') {
34
+ return [
35
+ absCommand,
36
+ segment[1],
37
+ segment[2],
38
+ segment[3],
39
+ segment[4],
40
+ segment[5],
41
+ (segment as ASegment)[6] + lastX,
42
+ (segment as ASegment)[7] + lastY,
43
+ ] as ASegment;
44
+ } else if (absCommand === 'V') {
45
+ return [absCommand, (segment as VSegment)[1] + lastY] as VSegment;
46
+ } else if (absCommand === 'H') {
47
+ return [absCommand, (segment as HSegment)[1] + lastX] as HSegment;
48
+ } else if (absCommand === 'L') {
49
+ return [absCommand, (segment as LSegment)[1] + lastX, (segment as LSegment)[2] + lastY] as LSegment;
50
+ } else {
51
+ // use brakets for `eslint: no-case-declaration`
52
+ // https://stackoverflow.com/a/50753272/803358
53
+ const absValues = (segment.slice(1) as number[]).map((n, j) => n + (j % 2 ? lastY : lastX));
54
+ // for c, s, q, t
55
+ return [absCommand as typeof absCommand | number].concat(absValues) as
56
+ | MSegment
57
+ | QSegment
58
+ | TSegment
59
+ | SSegment
60
+ | CSegment;
54
61
  }
55
-
56
- return segment as AbsoluteSegment;
57
62
  };
58
63
  export default absolutizeSegment;
@@ -116,9 +116,9 @@ const arcToCubic = (
116
116
  m2[0] = 2 * m1[0] - m2[0];
117
117
  m2[1] = 2 * m1[1] - m2[1];
118
118
  if (recursive) {
119
- return [...m2, ...m3, ...m4, ...res];
119
+ return [m2[0], m2[1], m3[0], m3[1], m4[0], m4[1]].concat(res);
120
120
  }
121
- res = [...m2, ...m3, ...m4, ...res];
121
+ res = [m2[0], m2[1], m3[0], m3[1], m4[0], m4[1]].concat(res);
122
122
  const newres = [];
123
123
  for (let i = 0, ii = res.length; i < ii; i += 1) {
124
124
  newres[i] = i % 2 ? rotateVector(res[i - 1], res[i], rad).y : rotateVector(res[i], res[i + 1], rad).x;
@@ -1,32 +1,57 @@
1
- import paramsParser from '../parser/paramsParser';
2
- import type { PathArray, PathCommand, PathSegment, IteratorCallback } from '../types';
1
+ // import paramsParser from '../parser/paramsParser';
2
+ import type { PathArray, PathCommand, PathSegment, IteratorCallback, AbsoluteCommand } from '../types';
3
3
 
4
4
  const iterate = <T extends PathArray>(path: PathArray, iterator: IteratorCallback) => {
5
- const allPathCommands = [] as PathCommand[];
6
- const params = { ...paramsParser };
7
5
  let pathLen = path.length;
8
6
  let segment: PathSegment;
9
7
  let pathCommand = 'M' as PathCommand;
8
+ let absCommand = 'M' as AbsoluteCommand;
9
+ let isRelative = false;
10
+ let x = 0;
11
+ let y = 0;
12
+ let mx = 0;
13
+ let my = 0;
14
+ let segLen = 0;
10
15
 
11
16
  for (let i = 0; i < pathLen; i += 1) {
12
17
  segment = path[i];
13
18
  [pathCommand] = segment;
14
- allPathCommands[i] = pathCommand;
15
- const iteratorResult = iterator(segment, params, i);
16
- path[i] = iteratorResult;
19
+ segLen = segment.length;
20
+ absCommand = pathCommand.toUpperCase() as AbsoluteCommand;
21
+ isRelative = absCommand !== pathCommand;
17
22
 
18
- if (iteratorResult[0] === 'C') {
19
- pathLen = path.length;
23
+ const iteratorResult = iterator(segment, i, x, y);
24
+ // some methods like getPointAtLength would like to break
25
+ // when task is complete
26
+ if (iteratorResult === false) {
27
+ break;
20
28
  }
21
29
 
22
- segment = path[i];
23
- const seglen = segment.length;
24
- params.x1 = +segment[seglen - 2];
25
- params.y1 = +segment[seglen - 1];
26
- params.x2 = +segment[seglen - 4] || params.x1;
27
- params.y2 = +segment[seglen - 3] || params.y1;
30
+ // segment = path[i];
31
+ if (absCommand === 'Z') {
32
+ x = mx;
33
+ y = my;
34
+ } else if (absCommand === 'H') {
35
+ x = (segment[1] as number) + (isRelative ? x : 0);
36
+ } else if (absCommand === 'V') {
37
+ y = (segment[1] as number) + (isRelative ? y : 0);
38
+ } else {
39
+ x = (segment[segLen - 2] as number) + (isRelative ? x : 0);
40
+ y = (segment[segLen - 1] as number) + (isRelative ? y : 0);
41
+
42
+ if (absCommand === 'M') {
43
+ mx = x;
44
+ my = y;
45
+ }
46
+ }
47
+
48
+ if (iteratorResult) {
49
+ path[i] = iteratorResult;
50
+ if (iteratorResult[0] === 'C') {
51
+ pathLen = path.length;
52
+ }
53
+ }
28
54
  }
29
- // console.log('iteration: ', ...path)
30
55
  return path as T;
31
56
  };
32
57
 
@@ -12,6 +12,6 @@ import midPoint from '../math/midPoint';
12
12
  const lineToCubic = (x1: number, y1: number, x2: number, y2: number) => {
13
13
  const c1 = midPoint([x1, y1], [x2, y2], 1.0 / 3.0);
14
14
  const c2 = midPoint([x1, y1], [x2, y2], 2.0 / 3.0);
15
- return [...c1, ...c2, x2, y2];
15
+ return [c1[0], c1[1], c2[0], c2[1], x2, y2];
16
16
  };
17
17
  export default lineToCubic;
@@ -1,11 +1,11 @@
1
1
  import normalizeSegment from './normalizeSegment';
2
- import type { AbsoluteCommand, NormalArray, PathArray, PointTuple } from '../types';
2
+ import type { NormalArray, PathArray } from '../types';
3
3
  import iterate from './iterate';
4
4
  import parsePathString from '../parser/parsePathString';
5
- import absolutizeSegment from './absolutizeSegment';
5
+ import paramsParser from '../parser/paramsParser';
6
6
 
7
7
  /**
8
- * Normalizes a `path` object for further processing:
8
+ * Normalizes a `pathArray` object for further processing:
9
9
  * * convert segments to absolute values
10
10
  * * convert shorthand path commands to their non-shorthand notation
11
11
  *
@@ -13,37 +13,20 @@ import absolutizeSegment from './absolutizeSegment';
13
13
  * @returns the normalized `pathArray`
14
14
  */
15
15
  const normalizePath = (pathInput: string | PathArray) => {
16
- let x = 0;
17
- let y = 0;
18
- let mx = 0;
19
- let my = 0;
20
- let pathCommand = 'M';
16
+ const path = parsePathString(pathInput);
17
+ const params = { ...paramsParser };
21
18
 
22
- return iterate<NormalArray>(parsePathString(pathInput), (seg, params) => {
23
- const absoluteSegment = absolutizeSegment(seg, params);
24
- const result = normalizeSegment(absoluteSegment, params);
25
- [pathCommand] = result;
26
- const absCommand = pathCommand.toUpperCase() as AbsoluteCommand;
19
+ return iterate<NormalArray>(path, (seg, _, lastX, lastY) => {
20
+ params.x = lastX;
21
+ params.y = lastY;
22
+ const result = normalizeSegment(seg, params);
27
23
 
28
- if (absCommand === 'Z') {
29
- x = mx;
30
- y = my;
31
- } else {
32
- [x, y] = result.slice(-2) as PointTuple;
24
+ const seglen = result.length;
25
+ params.x1 = +result[seglen - 2];
26
+ params.y1 = +result[seglen - 1];
27
+ params.x2 = +result[seglen - 4] || params.x1;
28
+ params.y2 = +result[seglen - 3] || params.y1;
33
29
 
34
- if (absCommand === 'M') {
35
- mx = x;
36
- my = y;
37
- }
38
- }
39
-
40
- // const seglen = result.length;
41
- // params.x1 = +result[seglen - 2];
42
- // params.y1 = +result[seglen - 1];
43
- // params.x2 = +result[seglen - 4] || params.x1;
44
- // params.y2 = +result[seglen - 3] || params.y1;
45
- params.x = x;
46
- params.y = y;
47
30
  return result;
48
31
  });
49
32
  };
@@ -1,46 +1,84 @@
1
1
  import type { ParserParams } from '../interface';
2
- import type { NormalSegment, PointTuple, PathSegment, QSegment, CSegment, LSegment } from '../types';
2
+ import type {
3
+ NormalSegment,
4
+ PointTuple,
5
+ PathSegment,
6
+ QSegment,
7
+ CSegment,
8
+ LSegment,
9
+ MSegment,
10
+ HSegment,
11
+ VSegment,
12
+ ASegment,
13
+ PathCommand,
14
+ } from '../types';
3
15
 
4
16
  /**
5
17
  * Normalizes a single segment of a `pathArray` object.
6
18
  *
7
19
  * @param segment the segment object
8
- * @param params the coordinates of the previous segment
20
+ * @param params the normalization parameters
9
21
  * @returns the normalized segment
10
22
  */
11
23
  const normalizeSegment = (segment: PathSegment, params: ParserParams) => {
12
24
  const [pathCommand] = segment;
13
- const { x1: px1, y1: py1, x2: px2, y2: py2 } = params;
14
- const values = segment.slice(1).map(Number);
25
+ const absCommand = pathCommand.toUpperCase();
26
+ const isRelative = pathCommand !== absCommand;
27
+ const { x1: px1, y1: py1, x2: px2, y2: py2, x, y } = params;
28
+ const values = segment.slice(1) as number[];
29
+ let absValues = values.map((n, j) => n + (isRelative ? (j % 2 ? y : x) : 0));
15
30
 
16
- if (!'TQ'.includes(pathCommand)) {
31
+ if (!'TQ'.includes(absCommand)) {
17
32
  // optional but good to be cautious
18
33
  params.qx = null;
19
34
  params.qy = null;
20
35
  }
21
36
 
22
- if (pathCommand === 'H') {
23
- return ['L', segment[1], py1] as LSegment;
24
- } else if (pathCommand === 'V') {
25
- return ['L', px1, segment[1]] as LSegment;
26
- } else if (pathCommand === 'S') {
37
+ // istanbul ignore else @preserve
38
+ if (absCommand === 'A') {
39
+ absValues = values.slice(0, -2).concat(values[5] + (isRelative ? x : 0), values[6] + (isRelative ? y : 0));
40
+
41
+ return ['A' as PathCommand | number].concat(absValues) as ASegment;
42
+ } else if (absCommand === 'H') {
43
+ return ['L', (segment as HSegment)[1] + (isRelative ? x : 0), py1] as LSegment;
44
+ } else if (absCommand === 'V') {
45
+ return ['L', px1, (segment as VSegment)[1] + (isRelative ? y : 0)] as LSegment;
46
+ } else if (absCommand === 'L') {
47
+ return [
48
+ 'L',
49
+ (segment as LSegment)[1] + (isRelative ? x : 0),
50
+ (segment as LSegment)[2] + (isRelative ? y : 0),
51
+ ] as LSegment;
52
+ } else if (absCommand === 'M') {
53
+ return [
54
+ 'M',
55
+ (segment as MSegment)[1] + (isRelative ? x : 0),
56
+ (segment as MSegment)[2] + (isRelative ? y : 0),
57
+ ] as MSegment;
58
+ } else if (absCommand === 'C') {
59
+ return ['C' as PathCommand | number].concat(absValues) as CSegment;
60
+ } else if (absCommand === 'S') {
27
61
  const x1 = px1 * 2 - px2;
28
62
  const y1 = py1 * 2 - py2;
29
63
  params.x1 = x1;
30
64
  params.y1 = y1;
31
- return ['C', x1, y1, ...values] as CSegment;
32
- } else if (pathCommand === 'T') {
65
+ return ['C', x1, y1].concat(absValues) as CSegment;
66
+ } else if (absCommand === 'T') {
33
67
  const qx = px1 * 2 - (params.qx ? params.qx : /* istanbul ignore next */ 0);
34
68
  const qy = py1 * 2 - (params.qy ? params.qy : /* istanbul ignore next */ 0);
35
69
  params.qx = qx;
36
70
  params.qy = qy;
37
- return ['Q', qx, qy, ...values] as QSegment;
38
- } else if (pathCommand === 'Q') {
39
- const [nqx, nqy] = values as PointTuple;
71
+ return ['Q', qx, qy].concat(absValues) as QSegment;
72
+ } else if (absCommand === 'Q') {
73
+ const [nqx, nqy] = absValues as PointTuple;
40
74
  params.qx = nqx;
41
75
  params.qy = nqy;
76
+ return ['Q' as PathCommand | number].concat(absValues) as QSegment;
77
+ } else if (absCommand === 'Z') {
78
+ return ['Z'] as NormalSegment;
42
79
  }
43
80
 
81
+ // istanbul ignore next @preserve
44
82
  return segment as NormalSegment;
45
83
  };
46
84
  export default normalizeSegment;
@@ -1,10 +1,11 @@
1
- import roundPath from './roundPath';
2
1
  import pathToAbsolute from '../convert/pathToAbsolute';
3
- import pathToRelative from '../convert/pathToRelative';
4
2
  import shortenSegment from './shortenSegment';
5
3
  import paramsParser from '../parser/paramsParser';
6
- import normalizePath from './normalizePath';
7
- import type { PathSegment, HSegment, PathArray, VSegment, PathCommand, AbsoluteSegment } from '../types';
4
+ import type { AbsoluteSegment, PathArray, PathCommand } from '../types';
5
+ import iterate from './iterate';
6
+ import normalizeSegment from './normalizeSegment';
7
+ import relativizeSegment from './relativizeSegment';
8
+ import roundSegment from './roundSegment';
8
9
 
9
10
  /**
10
11
  * Optimizes a `pathArray` object:
@@ -12,72 +13,51 @@ import type { PathSegment, HSegment, PathArray, VSegment, PathCommand, AbsoluteS
12
13
  * * select shortest segments from absolute and relative `pathArray`s
13
14
  *
14
15
  * @param pathInput a string or `pathArray`
15
- * @param round the amount of decimals to round values to
16
+ * @param roundOption the amount of decimals to round values to
16
17
  * @returns the optimized `pathArray`
17
18
  */
18
- const optimizePath = (pathInput: PathArray, round: 'off' | number): PathArray => {
19
+ const optimizePath = (pathInput: PathArray, roundOption: number) => {
19
20
  const path = pathToAbsolute(pathInput);
20
- const normalPath = normalizePath(path);
21
- const params = { ...paramsParser };
21
+ // allow for ZERO decimals or use an aggressive value of 2
22
+ const round =
23
+ typeof roundOption === 'number' && roundOption >= 0 ? roundOption : /* istanbul ignore next @preserve */ 2;
24
+ // this utility overrides the iterator params
25
+ const optimParams = { ...paramsParser };
26
+
22
27
  const allPathCommands = [] as PathCommand[];
23
- const ii = path.length;
24
- let pathCommand = '' as PathCommand;
25
- let prevCommand = '' as PathCommand;
26
- let x = 0;
27
- let y = 0;
28
- let mx = 0;
29
- let my = 0;
28
+ let pathCommand = 'M' as PathCommand;
29
+ let prevCommand = 'Z' as PathCommand;
30
30
 
31
- for (let i = 0; i < ii; i += 1) {
32
- [pathCommand] = path[i];
31
+ return iterate(path, (seg, i, lastX, lastY) => {
32
+ optimParams.x = lastX;
33
+ optimParams.y = lastY;
34
+ // const absoluteSegment = absolutizeSegment(seg, optimParams);
35
+ const normalizedSegment = normalizeSegment(seg, optimParams);
36
+ let result = seg;
37
+ [pathCommand] = seg;
33
38
 
34
39
  // Save current path command
35
40
  allPathCommands[i] = pathCommand;
36
- // Get previous path command for `shortenSegment`
37
- if (i) prevCommand = allPathCommands[i - 1];
38
- path[i] = shortenSegment(path[i], normalPath[i], params, prevCommand) as AbsoluteSegment;
39
-
40
- const segment = path[i];
41
- const seglen = segment.length;
42
-
43
- // update C, S, Q, T specific params
44
- params.x1 = +segment[seglen - 2];
45
- params.y1 = +segment[seglen - 1];
46
- params.x2 = +segment[seglen - 4] || params.x1;
47
- params.y2 = +segment[seglen - 3] || params.y1;
48
-
49
- // update x, y params
50
- switch (pathCommand) {
51
- case 'Z':
52
- x = mx;
53
- y = my;
54
- break;
55
- case 'H':
56
- [, x] = segment as HSegment;
57
- break;
58
- case 'V':
59
- [, y] = segment as VSegment;
60
- break;
61
- default:
62
- [x, y] = segment.slice(-2).map(Number);
63
-
64
- if (pathCommand === 'M') {
65
- mx = x;
66
- my = y;
67
- }
41
+ if (i) {
42
+ // Get previous path command for `shortenSegment`
43
+ prevCommand = allPathCommands[i - 1];
44
+ const shortSegment = shortenSegment(seg as AbsoluteSegment, normalizedSegment, optimParams, prevCommand);
45
+ const absSegment = roundSegment(shortSegment, round);
46
+ const absString = absSegment.join('');
47
+ const relativeSegment = relativizeSegment(shortSegment, i, lastX, lastY);
48
+ const relSegment = roundSegment(relativeSegment, round);
49
+ const relString = relSegment.join('');
50
+ result = absString.length < relString.length ? absSegment : relSegment;
68
51
  }
69
- params.x = x;
70
- params.y = y;
71
- }
72
52
 
73
- const absolutePath = roundPath(path, round);
74
- const relativePath = roundPath(pathToRelative(path), round);
53
+ const seglen = normalizedSegment.length;
54
+ optimParams.x1 = +normalizedSegment[seglen - 2];
55
+ optimParams.y1 = +normalizedSegment[seglen - 1];
56
+ optimParams.x2 = +normalizedSegment[seglen - 4] || optimParams.x1;
57
+ optimParams.y2 = +normalizedSegment[seglen - 3] || optimParams.y1;
75
58
 
76
- return absolutePath.map((a: PathSegment, i: number) => {
77
- if (i) {
78
- return a.join('').length < relativePath[i].join('').length ? a : relativePath[i];
79
- }
80
- return a;
81
- }) as PathArray;
59
+ return result;
60
+ });
82
61
  };
62
+
83
63
  export default optimizePath;
@@ -13,7 +13,7 @@ import { type PointTuple } from '../types';
13
13
  * @return the resulting Tuple
14
14
  */
15
15
  const translatePoint = (cssm: CSSMatrix, v: [number, number, number, number]): [number, number, number, number] => {
16
- let m = CSSMatrix.Translate(...(v.slice(0, -1) as [number, number, number]));
16
+ let m = CSSMatrix.Translate(v[0], v[1], v[2]);
17
17
 
18
18
  [, , , m.m44] = v;
19
19
  m = cssm.multiply(m);
@@ -37,7 +37,7 @@ const translatePoint = (cssm: CSSMatrix, v: [number, number, number, number]): [
37
37
  */
38
38
  const projection2d = (m: CSSMatrix, point2D: PointTuple, origin: [number, number, number]): PointTuple => {
39
39
  const [originX, originY, originZ] = origin;
40
- const [x, y, z] = translatePoint(m, [...point2D, 0, 1]);
40
+ const [x, y, z] = translatePoint(m, [point2D[0], point2D[1], 0, 1]);
41
41
 
42
42
  const relativePositionX = x - originX;
43
43
  const relativePositionY = y - originY;