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
@@ -3,7 +3,7 @@ const simpleShapes = {
3
3
  'M10 10l80 80V10H50', // line
4
4
  'M10 90C30 90 25 10 50 10s20 80 40 80s15 -80 40 -80s20 80 40 80', // cubic
5
5
  'M10 50q15 -25 30 0t30 0t30 0t30 0t30 0t30 0', // quad
6
- 'M6 10a6 4 10 1 0 8 0M6 10a6 4 10 1 1 8 0M6 10a6 4 10 0 1 8 0M6 10a6 4 10 0 0 8 0' // arc
6
+ 'M6 10a6 4 10 1 0 8 0m-8 0a6 4 10 1 1 8 0m-8 0a6 4 10 0 1 8 0m-8 0a6 4 10 0 0 8 0' // arc
7
7
  ],
8
8
  // {translate: 15, rotate: 15, scale: 0.5}
9
9
  normalized: [
@@ -13,32 +13,32 @@ const simpleShapes = {
13
13
  "M6 10A6 4 10 1 0 14 10M6 10A6 4 10 1 1 14 10M6 10A6 4 10 0 1 14 10M6 10A6 4 10 0 0 14 10"
14
14
  ],
15
15
  transformed: [
16
- "M50.86 25.51L79.14 74.49L89.49 35.86L70.18 30.68",
17
- "M61.19 58.97C70.85 61.55 78.78 22.27 90.86 25.51S90.16 66.73 99.82 69.32S117.42 32.62 129.49 35.86S128.8 77.08 138.46 79.67",
18
- "M71.53 38.35Q82.01 28.22 86.02 42.24T100.51 46.12T115 50T129.49 53.88T143.98 57.76T158.47 61.65",
19
- "M23.07 9.48C21.4 10.14 21.89 12.25 23.95 13.28C26.02 14.32 28.1 13.5 27.71 11.81C27.61 11.37 27.34 10.92 26.93 10.52M23.07 9.48C21.52 7.97 22.24 6.18 24.36 6.27C26.48 6.35 28.41 8.25 27.84 9.68C27.69 10.05 27.37 10.35 26.93 10.52M23.07 9.48C24.2 9.04 25.88 9.49 26.93 10.52M23.07 9.48C24.12 10.51 25.8 10.96 26.93 10.52"
16
+ "M18.54 6.12L46.82 55.11L57.17 16.48L37.85 11.3",
17
+ "M8.18 44.76C17.84 47.35 25.78 8.06 37.85 11.3S37.16 52.53 46.82 55.11S64.42 18.42 76.49 21.65S75.8 62.88 85.46 65.47",
18
+ "M13.36 25.44Q23.84 15.31 27.85 29.32T42.34 33.21T56.83 37.09T71.31 40.97T85.8 44.85T100.29 48.74",
19
+ "M16.6 5.61C14.93 6.26 15.42 8.37 17.49 9.41C19.55 10.44 21.64 9.62 21.25 7.93C21.14 7.49 20.87 7.04 20.47 6.64M16.6 5.61C15.06 4.09 15.78 2.3 17.9 2.39C20.02 2.48 21.95 4.37 21.37 5.8C21.22 6.18 20.91 6.47 20.47 6.64M16.6 5.61C17.74 5.16 19.42 5.61 20.47 6.64M16.6 5.61C17.65 6.63 19.33 7.09 20.47 6.64"
20
20
  ],
21
21
  // {scale: [0.55,0.6,0.65]}
22
22
  scaled3d: [
23
- "M28 26L72 74V26H50",
24
- "M46 74C57 74 54.25 26 68 26S79 74 90 74S98.25 26 112 26S123 74 134 74",
25
- "M50.5 50Q58.75 35 67 50T83.5 50T100 50T116.5 50T133 50T149.5 50",
26
- "M7.8 10C6.21 11.28 7.33 13.57 9.82 14.13C12.31 14.69 14.29 13.09 13.4 11.25C13.16 10.77 12.74 10.34 12.2 10M7.8 10C5.73 8.72 5.98 6.43 8.26 5.87C10.54 5.31 13.13 6.91 12.92 8.75C12.87 9.23 12.62 9.66 12.2 10M7.8 10C8.88 9.13 10.79 9.13 12.2 10M7.8 10C9.21 10.87 11.12 10.87 12.2 10"
23
+ "M5.5 6L49.5 54V6H27.5",
24
+ "M5.5 54C16.5 54 13.75 6 27.5 6S38.5 54 49.5 54S57.75 6 71.5 6S82.5 54 93.5 54",
25
+ "M5.5 30Q13.75 15 22 30T38.5 30T55 30T71.5 30T88 30T104.5 30",
26
+ "M3.3 6C1.71 7.28 2.83 9.57 5.32 10.13C7.81 10.69 9.79 9.09 8.9 7.25C8.66 6.77 8.24 6.34 7.7 6M3.3 6C1.23 4.72 1.48 2.43 3.76 1.87C6.04 1.31 8.63 2.91 8.42 4.75C8.37 5.23 8.12 5.66 7.7 6M3.3 6C4.38 5.13 6.29 5.13 7.7 6M3.3 6C4.71 6.87 6.62 6.87 7.7 6"
27
27
  ],
28
28
  // {skew: 45}
29
29
  // {skew: [45,0]}
30
30
  skewedX: [
31
- "M-30 10L130 90L50 10H10",
32
- "M50 90C70 90 -15 10 10 10S110 90 130 90S65 10 90 10S190 90 210 90",
33
- "M10 50Q0 25 40 50T70 50T100 50T130 50T160 50T190 50",
34
- "M6 10C5.24 12.13 11.11 15.95 16.56 16.88C22.01 17.81 22.96 15.15 18.26 12.09C17.03 11.29 15.55 10.56 14 10M6 10C0.1 7.87 -3.26 4.05 -0.04 3.12C3.17 2.19 10.54 4.85 13.22 7.91C13.93 8.71 14.2 9.44 14 10M6 10C6.52 8.56 10 8.56 14 10M6 10C10 11.44 13.48 11.44 14 10"
31
+ "M20 10L180 90L100 10H60",
32
+ "M100 90C120 90 35 10 60 10S160 90 180 90S115 10 140 10S240 90 260 90",
33
+ "M60 50Q50 25 90 50T120 50T150 50T180 50T210 50T240 50",
34
+ "M16 10C15.24 12.13 21.11 15.95 26.56 16.88C32.01 17.81 32.96 15.15 28.26 12.09C27.03 11.29 25.55 10.56 24 10M16 10C10.1 7.87 6.74 4.05 9.96 3.12C13.17 2.19 20.54 4.85 23.22 7.91C23.93 8.71 24.2 9.44 24 10M16 10C16.52 8.56 20 8.56 24 10M16 10C20 11.44 23.48 11.44 24 10"
35
35
  ],
36
36
  // {skew: [0,45]}
37
37
  skewedY: [
38
- "M10 -30L90 130V50L50 10",
39
- "M10 10C30 30 25 -55 50 -30S70 70 90 90S105 25 130 50S150 150 170 170",
40
- "M10 -40Q25 -50 40 -10T70 20T100 50T130 80T160 110T190 140",
41
- "M6 6C3.11 5.24 5.15 11.11 9.67 16.56C14.2 22.01 17.81 22.96 16.17 18.26C15.75 17.03 14.99 15.55 14 14M6 6C2.23 0.1 2.7 -3.26 6.84 -0.04C10.98 3.17 15.69 10.54 15.31 13.22C15.22 13.93 14.76 14.2 14 14M6 6C7.96 6.52 11.44 10 14 14M6 6C8.56 10 12.04 13.48 14 14"
38
+ "M10 20L90 180V100L50 60",
39
+ "M10 100C30 120 25 35 50 60S70 160 90 180S105 115 130 140S150 240 170 260",
40
+ "M10 60Q25 50 40 90T70 120T100 150T130 180T160 210T190 240",
41
+ "M6 16C3.11 15.24 5.15 21.11 9.67 26.56C14.2 32.01 17.81 32.96 16.17 28.26C15.75 27.03 14.99 25.55 14 24M6 16C2.23 10.1 2.7 6.74 6.84 9.96C10.98 13.17 15.69 20.54 15.31 23.22C15.22 23.93 14.76 24.2 14 24M6 16C7.96 16.52 11.44 20 14 24M6 16C8.56 20 12.04 23.48 14 24"
42
42
  ],
43
43
  reversed: [
44
44
  "M50 10H90V90L10 10",
@@ -68,7 +68,7 @@ const simpleShapes = {
68
68
  { x: 45.35533905932737, y: 45.35533905932737 },
69
69
  { x: 28.85808806500117, y: 46.6836898667648 },
70
70
  { x: 46.84696090751847, y: 58.807108530914896 },
71
- { x: 16.19278384041914, y: 14.935425084181293 }
71
+ { x: 7.697454886004856, y: 9.193767828563768 }
72
72
  ]
73
73
  };
74
74
 
@@ -1,13 +1,11 @@
1
1
  import { expect, it, describe, beforeEach, vi } from 'vitest';
2
- import SVGPathCommander, { type CurveArray, type ShapeTypes } from '~/index';
2
+ import SVGPathCommander, { PathArray, type CurveArray, type ShapeTypes } from '~/index';
3
3
  import invalidPathValue from '../src/parser/invalidPathValue';
4
- import getCubicProperties from '../src/math/cubicTools';
5
- import getQuadProperties from '../src/math/quadTools';
6
- import getArcProperties from '../src/math/arcTools';
7
4
  import error from '../src/parser/error';
8
5
 
9
6
  import getMarkup from './fixtures/getMarkup';
10
7
  import simpleShapes from './fixtures/simpleShapes';
8
+ import shapes from './fixtures/shapes';
11
9
  import shapeObjects from './fixtures/shapeObjects';
12
10
 
13
11
  import "../docs/assets/style.css";
@@ -117,11 +115,12 @@ describe('SVGPathCommander Static Methods', () => {
117
115
  });
118
116
  });
119
117
 
120
- it(`Can revert back to default round option`, () => {
121
- const sample = [["M", 0, 0], ["L", 181.99955, 0], ["L", 91, 72], ["L", 0, 0], ["Z"]];
122
- const rounded = [["M", 0, 0], ["L", 181.9996, 0], ["L", 91, 72], ["L", 0, 0], ["Z"]];
118
+ it(`Can disable round, use a given decimal amount or revert back to default round option`, () => {
119
+ const sample = [["M", 0, 0], ["L", 181.99955, 0], ["L", 91, 72], ["L", 0, 0], ["Z"]] as PathArray;
120
+ const rounded = [["M", 0, 0], ["L", 181.9996, 0], ["L", 91, 72], ["L", 0, 0], ["Z"]] as PathArray;
123
121
 
124
- // @ts-expect-error
122
+ expect(SVGPathCommander.roundPath(sample, 4), `can use number setting`).to.deep.equal(rounded);
123
+ expect(SVGPathCommander.roundPath(sample, 'off'), `can use "off" setting`).to.deep.equal(sample);
125
124
  expect(SVGPathCommander.roundPath(sample, -1), `use 4 decimals when negative number is provided`).to.deep.equal(rounded);
126
125
  // @ts-expect-error
127
126
  expect(SVGPathCommander.roundPath(sample, 'wombat'), `use 4 decimals when string is provided`).to.deep.equal(rounded);
@@ -200,7 +199,7 @@ describe('SVGPathCommander Static Methods', () => {
200
199
  const propsPoint0 = getPropertiesAtPoint(simpleShapes.initial[1], { "x": 10, "y": 90 });
201
200
  expect(propsPoint0.closest).to.deep.equal({ x: 10, y: 90 });
202
201
  expect(propsPoint0.distance).to.equal(0);
203
- expect(propsPoint0.segment).to.deep.equal({ segment: ["M", 10, 90], index: 0, length: 0, point: { x: 10, y: 90 }, lengthAtSegment: 0 })
202
+ expect(propsPoint0.segment).to.deep.equal({ segment: ["M", 10, 90], index: 0, length: 0, lengthAtSegment: 0 })
204
203
 
205
204
  // getPropertiesAtPoint mid point
206
205
  const propsPoint50 = getPropertiesAtPoint(simpleShapes.initial[1], { x: 30.072453006153214, y: 41.42818552481854 });
@@ -212,7 +211,7 @@ describe('SVGPathCommander Static Methods', () => {
212
211
  const propsPoint400 = getPropertiesAtPoint(simpleShapes.initial[1], { "x": 50, "y": 10 });
213
212
  expect(propsPoint400.closest).to.deep.equal({ x: 50.000003520199236, y: 10.000000000000531 })
214
213
  expect(propsPoint400.distance).to.equal(0.0000035201992361067316)
215
- expect(propsPoint400.segment).to.deep.equal({ segment: ['s', 20, 80, 40, 80], index: 2, length: 94.75724347727943, lengthAtSegment: 94.75724347727943})
214
+ expect(propsPoint400.segment).to.deep.equal({ segment: ['s', 20, 80, 40, 80], index: 2, length: 94.75724347727943, lengthAtSegment: 94.75724347727943 })
216
215
  });
217
216
 
218
217
  it(`Can do getSegmentAtLength`, () => {
@@ -226,9 +225,9 @@ describe('SVGPathCommander Static Methods', () => {
226
225
  it(`Can do getSegmentOfPoint`, () => {
227
226
  const { getSegmentOfPoint } = SVGPathCommander;
228
227
  // first point
229
- expect(getSegmentOfPoint(simpleShapes.initial[1], { x: 10, y: 90 })).to.deep.equal({ segment: ["M", 10, 90], index: 0, length: 0, point: { x: 10, y: 90 }, lengthAtSegment: 0 });
228
+ expect(getSegmentOfPoint(simpleShapes.initial[1], { x: 10, y: 90 })).to.deep.equal({ segment: ["M", 10, 90], index: 0, length: 0, lengthAtSegment: 0 });
230
229
  // mid point
231
- expect(getSegmentOfPoint(simpleShapes.initial[3], { x: 9, y: 9 })).to.deep.equal({ segment: ["a", 6, 4, 10, 0, 0, 8, 0], index: 7, length: 7.498916687913066,/* point: { x: 6, y: 10 },*/ lengthAtSegment: 55.613707646817915 });
230
+ expect(getSegmentOfPoint(simpleShapes.initial[3], { x: 9, y: 9 })).to.deep.equal({ segment: ["a", 6, 4, 10, 0, 1, 8, 0], index: 5, length: 7.498916687913066,/* point: { x: 6, y: 10 },*/ lengthAtSegment: 48.11479095890485 });
232
231
  });
233
232
 
234
233
  it(`Can do getClosestPoint`, () => {
@@ -236,7 +235,7 @@ describe('SVGPathCommander Static Methods', () => {
236
235
  // first point
237
236
  expect(getClosestPoint(simpleShapes.initial[1], { x: 10, y: 90 })).to.deep.equal({ x: 10, y: 90 });
238
237
  // mid point
239
- expect(getClosestPoint(simpleShapes.initial[3], { x: 9, y: 9 })).to.deep.equal({ x: 8.648537607602185, y: 10.940998877338636 });
238
+ expect(getClosestPoint(simpleShapes.initial[3], { x: 9, y: 9 })).to.deep.equal({ x: 8.995511191469355, y: 8.952970323068374 });
240
239
  });
241
240
 
242
241
  it(`Can do isPointInStroke`, () => {
@@ -245,7 +244,7 @@ describe('SVGPathCommander Static Methods', () => {
245
244
  expect(isPointInStroke(simpleShapes.initial[1], { x: 10, y: 90 })).to.be.true;
246
245
  // mid point'
247
246
  // expect(isPointInStroke(simpleShapes.initial[1], { x: 28.94438057441916, y: 46.29922469345143 })).to.be.true;
248
- expect(isPointInStroke(simpleShapes.initial[1], {x: 90, y: 90})).to.be.true;
247
+ expect(isPointInStroke(simpleShapes.initial[1], { x: 90, y: 90 })).to.be.true;
249
248
  // ({ x: 10, y: 10 })
250
249
  expect(isPointInStroke(simpleShapes.initial[1], { x: 10, y: 10 })).to.be.false;
251
250
  // ({ x: 45.355339, y: 45.355339 })
@@ -269,13 +268,13 @@ describe('SVGPathCommander Static Methods', () => {
269
268
  });
270
269
 
271
270
  it(`Can do polygonLength`, () => {
272
- const { polygonLength } = SVGPathCommander;
273
- expect(polygonLength([[100, 100], [150, 25], [150, 75], [200, 0]])).to.equal(230.27756377319946);
271
+ const { polygonTools } = SVGPathCommander;
272
+ expect(polygonTools.polygonLength([[100, 100], [150, 25], [150, 75], [200, 0]])).to.equal(230.27756377319946);
274
273
  });
275
274
 
276
275
  it(`Can do polygonArea`, () => {
277
- const { polygonArea } = SVGPathCommander;
278
- expect(polygonArea([[107.4, 13], [113.7, 28.8], [127.9, 31.3], [117.6, 43.5], [120.1, 60.8], [107.4, 52.6], [94.6, 60.8], [97.1, 43.5], [86.8, 31.3], [101, 28.8]])).to.equal(-836.69);
276
+ const { polygonTools } = SVGPathCommander;
277
+ expect(polygonTools.polygonArea([[107.4, 13], [113.7, 28.8], [127.9, 31.3], [117.6, 43.5], [120.1, 60.8], [107.4, 52.6], [94.6, 60.8], [97.1, 43.5], [86.8, 31.3], [101, 28.8]])).to.equal(-836.69);
279
278
  });
280
279
 
281
280
  it(`Can do transformPath with empty object`, () => {
@@ -288,17 +287,44 @@ describe('SVGPathCommander Static Methods', () => {
288
287
  expect(path2).to.equal(simpleShapes.normalized[0]);
289
288
  });
290
289
 
290
+ it(`Can check path type`, () => {
291
+ const { parsePathString, isPathArray, isCurveArray, isAbsoluteArray, isRelativeArray, isNormalizedArray } = SVGPathCommander;
292
+ // const
293
+ expect(isPathArray(simpleShapes.initial[0])).to.be.false;
294
+ expect(isPathArray(parsePathString(simpleShapes.initial[0]))).to.be.true;
295
+ expect(isAbsoluteArray(simpleShapes.normalized[0])).to.be.false;
296
+ expect(isAbsoluteArray(parsePathString(simpleShapes.normalized[0]))).to.be.true;
297
+ expect(isRelativeArray(shapes.relative[7])).to.be.false;
298
+ // console.log(parsePathString(shapes.relative[7]))
299
+ expect(isRelativeArray(parsePathString(shapes.relative[7]))).to.be.true;
300
+ expect(isNormalizedArray(simpleShapes.normalized[0])).to.be.false;
301
+ expect(isNormalizedArray(parsePathString(simpleShapes.normalized[0]))).to.be.true;
302
+ expect(isCurveArray(simpleShapes.normalized[1])).to.be.false;
303
+ expect(isCurveArray(parsePathString(simpleShapes.normalized[1]))).to.be.true;
304
+
305
+ });
306
+
291
307
  it(`Can cover all remaining branches`, () => {
292
- const { pathFactory, getPointAtLength, getTotalLength } = SVGPathCommander;
293
- const path = pathFactory(simpleShapes.normalized[0] as string, undefined);
294
- expect(path.length).to.be.above(0);
308
+ const { splitPath, pathToString, optimizePath, parsePathString, getPathBBox, getPointAtLength, getTotalLength } = SVGPathCommander;
309
+ expect(getPointAtLength(simpleShapes.normalized[3], 24.057395479452424)).to.deep.equal({ x: 14, y: 10 });
295
310
  expect(getPointAtLength(simpleShapes.normalized[0], 0)).to.deep.equal({ x: 10, y: 10 });
296
- expect(getTotalLength(simpleShapes.normalized[0])).to.be.above(233);
297
- const cubic = getCubicProperties(16, 8, 16, 4.134, 12.418, 1, 8, 1, undefined);
298
- expect(cubic.length).to.be.above(0);
299
- const quad = getQuadProperties(8, 1.34, 10.75, 1.34, 12.7, 3.29, undefined);
300
- expect(quad.length).to.be.above(0);
301
- const arc = getArcProperties(10, 0, 0.5, 0.5, 0, 0, 1, 10, 1, undefined);
302
- expect(arc.length).to.be.above(0);
311
+ expect(getPointAtLength(simpleShapes.normalized[3], undefined)).to.deep.equal({ x: 6, y: 10 });
312
+ expect(getTotalLength(simpleShapes.normalized[0])).to.be.above(233);
313
+ expect(getPathBBox(simpleShapes.normalized[2])).to.deep.equal({
314
+ "cx": 100, "cy": 50, "cz": 192.5,
315
+ "height": 25, "width": 180,
316
+ "x": 10, "y": 37.5, "x2": 190, "y2": 62.5,
317
+ });
318
+ expect(getPathBBox(simpleShapes.normalized[1])).to.deep.equal({
319
+ "cx": 90, "cy": 50, "cz": 200,
320
+ "height": 80, "width": 160,
321
+ "x": 10, "y": 10, "x2": 170,"y2": 90,
322
+ });
323
+ expect(splitPath(parsePathString(shapes.relative[1])).length).to.equal(7);
324
+ expect(pathToString(optimizePath(parsePathString(
325
+ // 'M10 50q15 -25 30 0Q55 75 70 50Q85 25 100 50T130 50Q145 25 160 50t30 0'
326
+ simpleShapes.normalized[2]
327
+ ), 2))).to.equal(simpleShapes.initial[2]);
328
+ // M10 50q15 -25 30 0t30 0t30 0t30 0t30 0t30 0
303
329
  });
304
330
  });
package/cypress.config.ts DELETED
@@ -1,29 +0,0 @@
1
- import { defineConfig } from "cypress";
2
- import createBundler from "@bahmutov/cypress-esbuild-preprocessor";
3
- import createEsbuildIstanbulPlugin from "./cypress/plugins/esbuild-istanbul";
4
-
5
- async function setupNodeEvents(
6
- on: Cypress.PluginEvents,
7
- config: Cypress.PluginConfigOptions
8
- ): Promise<Cypress.PluginConfigOptions> {
9
- await require("@cypress/code-coverage/task")(on, config);
10
-
11
- on(
12
- "file:preprocessor",
13
- createBundler({
14
- plugins: [createEsbuildIstanbulPlugin()],
15
- })
16
- );
17
-
18
- // Make sure to return the config object as it might have been modified by the plugin.
19
- return config;
20
- }
21
-
22
- export default defineConfig({
23
- e2e: {
24
- specPattern: "cypress/e2e/**/*.{js,jsx,ts,tsx}",
25
- supportFile: "cypress/support/e2e.ts",
26
- video: false,
27
- setupNodeEvents,
28
- },
29
- });
@@ -1,27 +0,0 @@
1
- /**
2
- * d3-polygon-area
3
- * https://github.com/d3/d3-polygon
4
- *
5
- * Returns the area of a polygon.
6
- *
7
- * @param polygon an array of coordinates
8
- * @returns the polygon area
9
- */
10
- const polygonArea = (polygon: [number, number][]): number => {
11
- const n = polygon.length;
12
- let i = -1;
13
- let a;
14
- let b = polygon[n - 1];
15
- let area = 0;
16
-
17
- /* eslint-disable-next-line */
18
- while (++i < n) {
19
- a = b;
20
- b = polygon[i];
21
- area += a[1] * b[0] - a[0] * b[1];
22
- }
23
-
24
- return area / 2;
25
- };
26
-
27
- export default polygonArea;
@@ -1,21 +0,0 @@
1
- import distanceSquareRoot from './distanceSquareRoot';
2
-
3
- /**
4
- * d3-polygon-length
5
- * https://github.com/d3/d3-polygon
6
- *
7
- * Returns the perimeter of a polygon.
8
- *
9
- * @param polygon an array of coordinates
10
- * @returns {number} the polygon length
11
- */
12
- const polygonLength = (polygon: [number, number][]): number => {
13
- return polygon.reduce((length, point, i) => {
14
- if (i) {
15
- return length + distanceSquareRoot(polygon[i - 1], point);
16
- }
17
- return 0;
18
- }, 0);
19
- };
20
-
21
- export default polygonLength;
@@ -1,23 +0,0 @@
1
- import { CSegment, PathArray, PathCommand } from '../types';
2
-
3
- /**
4
- * Splits an extended A (arc-to) segment into two cubic-bezier segments.
5
- *
6
- * @param path the `pathArray` this segment belongs to
7
- * @param allPathCommands all previous path commands
8
- * @param i the segment index
9
- */
10
- const fixArc = (path: PathArray, allPathCommands: PathCommand[], i: number) => {
11
- if (path[i].length > 7) {
12
- path[i].shift();
13
- const segment = path[i];
14
- let ni = i; // ESLint
15
- while (segment.length) {
16
- // if created multiple C:s, their original seg is saved
17
- allPathCommands[i] = 'A';
18
- path.splice((ni += 1), 0, ['C', ...segment.splice(0, 6)] as CSegment);
19
- }
20
- path.splice(i, 1);
21
- }
22
- };
23
- export default fixArc;
@@ -1,52 +0,0 @@
1
- import type { PathArray, PathCommand, PathSegment } from '../types';
2
- import isNormalizedArray from '../util/isNormalizedArray';
3
- import segmentToCubic from './segmentToCubic';
4
- import paramsParser from '../parser/paramsParser';
5
- import normalizePath from './normalizePath';
6
- import fixArc from './fixArc';
7
- import isAbsoluteArray from '../util/isAbsoluteArray';
8
- import pathToAbsolute from '../convert/pathToAbsolute';
9
-
10
- const replaceArc = (pathInput: PathArray | string): PathArray => {
11
- const absolutePath = isAbsoluteArray(pathInput) ? pathInput : pathToAbsolute(pathInput);
12
- const normalizedPath = isNormalizedArray(absolutePath) ? absolutePath : normalizePath(absolutePath);
13
- const params = { ...paramsParser };
14
- const allPathCommands = [] as PathCommand[]; // needed for arc to curve transformation
15
- let segment = [] as unknown as PathSegment;
16
- let seglen = 0;
17
- let pathCommand = '';
18
- const resultedPath = [] as unknown as PathArray;
19
- let i = 0;
20
- let ii = absolutePath.length;
21
-
22
- for (i = 0; i < ii; i += 1) {
23
- /* istanbul ignore else @preserve */
24
- if (absolutePath[i]) [pathCommand] = absolutePath[i];
25
- allPathCommands[i] = pathCommand as PathCommand;
26
-
27
- /* istanbul ignore else @preserve */
28
- if (pathCommand === 'A') {
29
- segment = segmentToCubic(normalizedPath[i], params);
30
-
31
- absolutePath[i] = segmentToCubic(normalizedPath[i], params);
32
- fixArc(absolutePath, allPathCommands, i);
33
-
34
- normalizedPath[i] = segmentToCubic(normalizedPath[i], params);
35
- fixArc(normalizedPath, allPathCommands, i);
36
- ii = Math.max(absolutePath.length, normalizedPath.length);
37
- }
38
-
39
- segment = normalizedPath[i];
40
- seglen = segment.length;
41
-
42
- params.x1 = +segment[seglen - 2];
43
- params.y1 = +segment[seglen - 1];
44
- params.x2 = +segment[seglen - 4] || params.x1;
45
- params.y2 = +segment[seglen - 3] || params.y1;
46
-
47
- resultedPath.push(absolutePath[i]);
48
- }
49
- return resultedPath;
50
- };
51
-
52
- export default replaceArc;
@@ -1,130 +0,0 @@
1
- import type { MSegment, PathArray, PathSegment, Point } from '../types';
2
- // import type { LengthFactory } from '../interface';
3
- import normalizePath from '../process/normalizePath';
4
- import getLineSegmentProperties from '../math/lineTools';
5
- import getArcSegmentProperties from '../math/arcTools';
6
- import getCubicSegmentProperties from '../math/cubicTools';
7
- import getQuadSegmentProperties from '../math/quadTools';
8
- import DISTANCE_EPSILON from './distanceEpsilon';
9
-
10
- /**
11
- * Returns a {x,y} point at a given length
12
- * of a shape, the shape total length and
13
- * the shape minimum and maximum {x,y} coordinates.
14
- *
15
- * @param pathInput the `pathArray` to look into
16
- * @param distance the length of the shape to look at
17
- * @returns the path length, point, min & max
18
- */
19
- const pathFactory = (pathInput: string | PathArray, distance?: number) => {
20
- const path = normalizePath(pathInput);
21
- const distanceIsNumber = typeof distance === 'number';
22
- let isM = false;
23
- let data = [] as number[];
24
- let pathCommand = 'M';
25
- let x = 0;
26
- let y = 0;
27
- let mx = 0;
28
- let my = 0;
29
- let seg = path[0] as PathSegment;
30
- const MIN = [] as Point[];
31
- const MAX = [] as Point[];
32
- let min = { x: 0, y: 0 };
33
- let max = { x: 0, y: 0 };
34
- let POINT = min;
35
- let LENGTH = 0;
36
- let props = {
37
- point: { x: 0, y: 0 },
38
- length: 0,
39
- bbox: {
40
- min: { x: 0, y: 0 },
41
- max: { x: 0, y: 0 },
42
- },
43
- };
44
-
45
- for (let i = 0, ll = path.length; i < ll; i += 1) {
46
- seg = path[i];
47
- [pathCommand] = seg;
48
- isM = pathCommand === 'M';
49
- data = !isM ? [x, y, ...(seg.slice(1) as number[])] : data;
50
-
51
- if (distanceIsNumber && distance < DISTANCE_EPSILON) {
52
- POINT = min;
53
- }
54
-
55
- // this segment is always ZERO
56
- /* istanbul ignore else @preserve */
57
- if (isM) {
58
- // remember mx, my for Z
59
- [, mx, my] = seg as MSegment;
60
- min = { x: mx, y: my };
61
- max = { x: mx, y: my };
62
- props = {
63
- point: min,
64
- length: 0,
65
- bbox: { min, max },
66
- };
67
- } else if (pathCommand === 'L') {
68
- props = getLineSegmentProperties(
69
- ...(data as [number, number, number, number]),
70
- distanceIsNumber ? distance - LENGTH : undefined,
71
- );
72
- } else if (pathCommand === 'A') {
73
- props = getArcSegmentProperties(
74
- ...(data as [number, number, number, number, number, number, number, number, number]),
75
- distanceIsNumber ? distance - LENGTH : undefined,
76
- );
77
- } else if (pathCommand === 'C') {
78
- props = getCubicSegmentProperties(
79
- ...(data as [number, number, number, number, number, number, number, number]),
80
- distanceIsNumber ? distance - LENGTH : undefined,
81
- );
82
- } else if (pathCommand === 'Q') {
83
- props = getQuadSegmentProperties(
84
- ...(data as [number, number, number, number, number, number]),
85
- distanceIsNumber ? distance - LENGTH : undefined,
86
- );
87
- } else if (pathCommand === 'Z') {
88
- data = [x, y, mx, my];
89
- props = getLineSegmentProperties(
90
- ...(data as [number, number, number, number]),
91
- distanceIsNumber ? distance - LENGTH : undefined,
92
- );
93
- }
94
-
95
- if (distanceIsNumber && LENGTH < distance && LENGTH + props.length >= distance) {
96
- POINT = props.point;
97
- }
98
-
99
- MIN.push(props.bbox.min);
100
- MAX.push(props.bbox.max);
101
- LENGTH += props.length;
102
-
103
- [x, y] = pathCommand !== 'Z' ? (seg.slice(-2) as [number, number]) : [mx, my];
104
- }
105
-
106
- // native `getPointAtLength` behavior when the given distance
107
- // is higher than total length
108
- if (distanceIsNumber && distance > LENGTH - DISTANCE_EPSILON) {
109
- POINT = { x, y };
110
- }
111
-
112
- return {
113
- point: POINT,
114
- length: LENGTH,
115
- get bbox() {
116
- return {
117
- min: {
118
- x: Math.min(...MIN.map(n => n.x)),
119
- y: Math.min(...MIN.map(n => n.y)),
120
- },
121
- max: {
122
- x: Math.max(...MAX.map(n => n.x)),
123
- y: Math.max(...MAX.map(n => n.y)),
124
- },
125
- };
126
- },
127
- };
128
- };
129
-
130
- export default pathFactory;