svg-path-commander 2.0.9 → 2.1.0

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 (59) hide show
  1. package/.eslintrc.cjs +1 -0
  2. package/README.md +4 -4
  3. package/dist/svg-path-commander.cjs +1 -1
  4. package/dist/svg-path-commander.cjs.map +1 -1
  5. package/dist/svg-path-commander.d.ts +156 -39
  6. package/dist/svg-path-commander.js +1 -1
  7. package/dist/svg-path-commander.js.map +1 -1
  8. package/dist/svg-path-commander.mjs +869 -705
  9. package/dist/svg-path-commander.mjs.map +1 -1
  10. package/package.json +22 -24
  11. package/src/convert/pathToAbsolute.ts +1 -1
  12. package/src/convert/pathToCurve.ts +1 -1
  13. package/src/convert/pathToRelative.ts +1 -1
  14. package/src/index.ts +30 -26
  15. package/src/interface.ts +32 -32
  16. package/src/math/arcTools.ts +217 -0
  17. package/src/math/bezier.ts +261 -0
  18. package/src/math/cubicTools.ts +81 -0
  19. package/src/math/lineTools.ts +52 -0
  20. package/src/math/quadTools.ts +79 -0
  21. package/src/parser/isMoveCommand.ts +17 -0
  22. package/src/parser/parsePathString.ts +1 -1
  23. package/src/parser/scanSegment.ts +12 -3
  24. package/src/process/normalizePath.ts +1 -1
  25. package/src/process/replaceArc.ts +52 -0
  26. package/src/process/splitPath.ts +1 -1
  27. package/src/process/transformPath.ts +14 -34
  28. package/src/types.ts +5 -0
  29. package/src/util/distanceEpsilon.ts +3 -0
  30. package/src/util/getClosestPoint.ts +1 -1
  31. package/src/util/getPathBBox.ts +4 -3
  32. package/src/util/getPointAtLength.ts +3 -3
  33. package/src/util/getPropertiesAtLength.ts +2 -1
  34. package/src/util/getPropertiesAtPoint.ts +4 -1
  35. package/src/util/getTotalLength.ts +2 -2
  36. package/src/util/isPointInStroke.ts +2 -1
  37. package/src/util/pathFactory.ts +130 -0
  38. package/src/util/shapeToPathArray.ts +8 -4
  39. package/test/class.test.ts +501 -0
  40. package/test/fixtures/getMarkup.ts +17 -0
  41. package/{cypress → test}/fixtures/shapes.js +18 -18
  42. package/{cypress → test}/fixtures/simpleShapes.js +6 -6
  43. package/test/static.test.ts +304 -0
  44. package/tsconfig.json +9 -4
  45. package/{vite.config.ts → vite.config.mts} +10 -1
  46. package/vitest.config-ui.mts +26 -0
  47. package/vitest.config.mts +26 -0
  48. package/cypress/e2e/svg-path-commander.spec.ts +0 -868
  49. package/cypress/plugins/esbuild-istanbul.ts +0 -50
  50. package/cypress/plugins/tsCompile.ts +0 -34
  51. package/cypress/support/commands.ts +0 -37
  52. package/cypress/support/e2e.ts +0 -21
  53. package/cypress/test.html +0 -36
  54. package/src/util/pathLengthFactory.ts +0 -114
  55. package/src/util/segmentArcFactory.ts +0 -219
  56. package/src/util/segmentCubicFactory.ts +0 -114
  57. package/src/util/segmentLineFactory.ts +0 -45
  58. package/src/util/segmentQuadFactory.ts +0 -109
  59. /package/{cypress/fixtures/shapeObjects.js → test/fixtures/shapeObjects.ts} +0 -0
@@ -0,0 +1,304 @@
1
+ import { expect, it, describe, beforeEach, vi } from 'vitest';
2
+ import SVGPathCommander, { type CurveArray, type ShapeTypes } from '~/index';
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
+ import error from '../src/parser/error';
8
+
9
+ import getMarkup from './fixtures/getMarkup';
10
+ import simpleShapes from './fixtures/simpleShapes';
11
+ import shapeObjects from './fixtures/shapeObjects';
12
+
13
+ import "../docs/assets/style.css";
14
+
15
+ describe('SVGPathCommander Static Methods', () => {
16
+ const wrapper = document.createElement('div');
17
+ document.body.append(wrapper);
18
+
19
+ beforeEach(async () => {
20
+ wrapper.innerHTML = '';
21
+ });
22
+
23
+ it('Convert shape to path with incomplete values should return false', () => {
24
+ ['line', 'circle', 'ellipse', 'rect', 'polygon', 'polyline', 'glyph'].forEach((SHAPE) => {
25
+ // @ts-expect-error
26
+ expect(SVGPathCommander.shapeToPath({ type: SHAPE, fill: 'red' }), `${SHAPE} with no specific attributes`).to.be.false;
27
+ })
28
+ });
29
+
30
+ it('Convert shape to pathArray with incomplete values should return false', () => {
31
+ ['line', 'circle', 'ellipse', 'rect', 'polygon', 'polyline', 'glyph'].forEach((SHAPE) => {
32
+ // @ts-expect-error
33
+ expect(SVGPathCommander.shapeToPathArray({ type: SHAPE, fill: 'red' }), `${SHAPE} with no specific attributes`).to.be.false;
34
+ })
35
+ });
36
+
37
+ ['wombat', 'line', 'circle', 'ellipse', 'rect', 'polygon', 'polyline', 'glyph', 'path'].forEach((SHAPE) => {
38
+ it(`Convert <${SHAPE}> to path`, async () => {
39
+ wrapper.append(getMarkup());
40
+ await vi.waitFor(() => wrapper.querySelector('svg') as SVGElement, { timeout: 200 });
41
+ const svg = await vi.waitFor(() => wrapper.querySelector('svg') as SVGElement, { timeout: 200 });
42
+ svg.setAttribute('viewBox', '0 0 182 72');
43
+ svg.innerHTML = `<line id="line" x1="0" y1="0" x2="182" y2="72" stroke="turquoise" stroke-width="2" />
44
+ <circle id="circle" cx="27.5" cy="36.9" r="23.5" fill="orangered"/>
45
+ <ellipse id="ellipse" cx="68.3" cy="37" rx="15.1" fill="darkorange"/>
46
+ <wombat id="wombat" fill="black"/>
47
+ <polygon id="polygon" points="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" fill="yellow"/>
48
+ <polyline id="polyline" points="107.39,17.78 112.43,30.42 123.79,32.42 115.55,42.18 117.55,56.02 107.39,49.46 97.15,56.02 99.15,42.18 90.91,32.42 102.27,30.42" fill="none" stroke="black" stroke-width="2"/>
49
+ <rect id="rect" x="131" y="13.2" width="47.5" height="47.6" rx="25" fill="yellowgreen"/>
50
+ <path id="path" d="M143.5 22.72H166s3 0 3 3v22.56s0 3 -3 3h-22.5s-3 0 -3 -3V25.72s0 -3 3 -3" fill="rgba(255,255,255,0.3)"/>
51
+ <glyph id="glyph" d="M143.5 22.72H166s3 0 3 3v22.56s0 3 -3 3h-22.5s-3 0 -3 -3V25.72s0 -3 3 -3" fill="rgba(255,255,255,0.3)"/>`;
52
+
53
+ const shape = await vi.waitFor(() => wrapper.querySelector(SHAPE) as SVGElement, { timeout: 200 });
54
+ if (SHAPE === 'wombat') {
55
+ try {
56
+ SVGPathCommander.shapeToPathArray(shape as unknown as SVGCircleElement, shape.ownerDocument);
57
+ } catch (er) {
58
+ expect(er).to.be.instanceOf(TypeError);
59
+ expect(er).to.have.property('message', `${error}: "${SHAPE}" is not SVGElement`);
60
+ }
61
+ try {
62
+ SVGPathCommander.shapeToPath(shape as unknown as SVGCircleElement, true, shape.ownerDocument);
63
+ } catch (er) {
64
+ expect(er).to.be.instanceOf(TypeError);
65
+ expect(er).to.have.property('message', `${error}: "${SHAPE}" is not SVGElement`);
66
+ }
67
+ } else if (SHAPE === 'path') {
68
+ try {
69
+ SVGPathCommander.shapeToPath(shape as unknown as SVGCircleElement, true, shape.ownerDocument);
70
+ } catch (er) {
71
+ expect(er).to.be.instanceOf(TypeError);
72
+ expect(er).to.have.property('message', `${error}: "${SHAPE}" is already SVGPathElement`);
73
+ }
74
+ } else {
75
+ SVGPathCommander.shapeToPath(shape as unknown as ShapeTypes, true, shape.ownerDocument);
76
+ expect(svg.querySelector(SHAPE)).to.not.exist;
77
+ expect(svg.querySelector(`#${SHAPE}`)).to.exist;
78
+ expect(svg.querySelector(`#${SHAPE}`)?.getAttribute('d')).to.have.length.greaterThan(0);
79
+ }
80
+ });
81
+ });
82
+
83
+ shapeObjects.forEach((SHAPE) => {
84
+ it(`Convert "${SHAPE.type}" Object to pathArray`, () => {
85
+ expect(SVGPathCommander.shapeToPathArray(SHAPE as unknown as ShapeTypes)).to.have.length.greaterThan(0);
86
+ });
87
+ });
88
+
89
+ it(`Convert <wombat> Object to pathArray should throw error`, () => {
90
+ try {
91
+ SVGPathCommander.shapeToPathArray({ type: 'wombat', fill: 'red' } as unknown as ShapeTypes)
92
+ } catch (er) {
93
+ expect(er).to.be.instanceOf(TypeError);
94
+ expect(er).to.have.property('message', `${error}: "wombat" is not SVGElement`);
95
+ }
96
+ });
97
+
98
+ shapeObjects.forEach((SHAPE) => {
99
+ it(`Convert "${SHAPE.type}" Object to path`, async () => {
100
+ wrapper.append(getMarkup());
101
+ await vi.waitFor(() => wrapper.querySelector('svg') as SVGElement, { timeout: 200 });
102
+ const svg = await vi.waitFor(() => wrapper.querySelector('svg') as SVGElement, { timeout: 200 });
103
+ svg.setAttribute('viewBox', '0 0 182 72');
104
+
105
+ svg.append(SVGPathCommander.shapeToPath(SHAPE as unknown as ShapeTypes) as SVGPathElement);
106
+ const shape = svg.querySelector(`#${SHAPE.type}`);
107
+ expect(shape).to.exist;
108
+ expect(shape?.getAttribute('d')).to.have.length.greaterThan(0);
109
+ });
110
+ });
111
+
112
+ simpleShapes.normalized.forEach((SHAPE, i) => {
113
+ it(`Can do optimizePath #${i}`, () => {
114
+ const path = new SVGPathCommander(SHAPE);
115
+
116
+ expect(path.optimize().toString()).to.equal(simpleShapes.initial[i]);
117
+ });
118
+ });
119
+
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"]];
123
+
124
+ // @ts-expect-error
125
+ expect(SVGPathCommander.roundPath(sample, -1), `use 4 decimals when negative number is provided`).to.deep.equal(rounded);
126
+ // @ts-expect-error
127
+ expect(SVGPathCommander.roundPath(sample, 'wombat'), `use 4 decimals when string is provided`).to.deep.equal(rounded);
128
+ });
129
+
130
+ it(`Can do reverseCurve`, () => {
131
+ const path = new SVGPathCommander(simpleShapes.normalized[1]);
132
+ const pathReversed = new SVGPathCommander(simpleShapes.normalized[1]).reverse();
133
+ const reversed = [["M", 170, 90], ["C", 150, 90, 155, 10, 130, 10], ["C", 105, 10, 110, 90, 90, 90], ["C", 70, 90, 75, 10, 50, 10], ["C", 25, 10, 30, 90, 10, 90]];
134
+
135
+ expect(pathReversed.segments).to.deep.equal(reversed);
136
+ expect(SVGPathCommander.reverseCurve(pathReversed.segments as CurveArray)).to.deep.equal(path.segments);
137
+ });
138
+
139
+ it(`Can do reversePath`, () => {
140
+ const path = new SVGPathCommander(simpleShapes.normalized[2]);
141
+ const pathReversed = new SVGPathCommander(simpleShapes.normalized[2]).reverse();
142
+ const reversed = [["M", 190, 50], ["Q", 175, 75, 160, 50], ["Q", 145, 25, 130, 50], ["Q", 115, 75, 100, 50], ["Q", 85, 25, 70, 50], ["Q", 55, 75, 40, 50], ["Q", 25, 25, 10, 50]];
143
+
144
+ expect(pathReversed.segments).to.deep.equal(reversed);
145
+ expect(SVGPathCommander.reversePath(pathReversed.segments)).to.deep.equal(path.segments);
146
+ });
147
+
148
+ it(`Can transformPath with arc segments`, () => {
149
+ const { transformPath, pathToString, reversePath } = SVGPathCommander;
150
+
151
+ // This test assumes **SVGPathCommander** static method can work with default
152
+ // transform origin *{0,0,0}* when no origin is provided in the **transformObject**.
153
+ // The test also shows how Arc segments are converted to CubicBezier for transformation.
154
+ const path = pathToString(transformPath(simpleShapes.initial[3], { rotate: 45 }));
155
+ expect(path).to.equal('M-2.8284 11.3137C-6.3778 10.7769 -7.6385 14.9238 -5.0977 18.7783C-2.5569 22.6327 1.8798 23.3038 2.8884 19.9862C3.153 19.1158 3.1321 18.0645 2.8284 16.9706M-2.8284 11.3137C-3.9859 7.1438 -0.9528 4.7691 2.631 7.0393C6.2148 9.3095 7.6616 14.5219 5.2352 16.4216C4.5986 16.92 3.7596 17.1114 2.8284 16.9706M-2.8284 11.3137C-0.4208 11.6779 2.0433 14.1419 2.8284 16.9706M-2.8284 11.3137C-2.0433 14.1423 0.4208 16.6064 2.8284 16.9706');
156
+
157
+ const path1 = pathToString(transformPath(simpleShapes.initial[3], { rotate: -45 }));
158
+ expect(path1).to.equal('M11.3137 2.8284C10.7769 6.3778 14.9238 7.6385 18.7783 5.0977C22.6327 2.5569 23.3038 -1.8798 19.9862 -2.8884C19.1158 -3.153 18.0645 -3.1321 16.9706 -2.8284M11.3137 2.8284C7.1438 3.9859 4.7691 0.9528 7.0393 -2.631C9.3095 -6.2148 14.5219 -7.6616 16.4216 -5.2352C16.92 -4.5986 17.1114 -3.7596 16.9706 -2.8284M11.3137 2.8284C11.6779 0.4208 14.1419 -2.0433 16.9706 -2.8284M11.3137 2.8284C14.1423 2.0433 16.6064 -0.4208 16.9706 -2.8284');
159
+
160
+ // @ts-expect-error
161
+ const path2 = pathToString(transformPath(reversePath(simpleShapes.initial[3]), { rotate: 45 }));
162
+ expect(path2).to.equal('M2.8284 16.9706C0.4208 16.6064 -2.0433 14.1423 -2.8284 11.3137M2.8284 16.9706C2.0433 14.1419 -0.4208 11.6779 -2.8284 11.3137M2.8284 16.9706C6.3778 17.5074 7.6385 13.3604 5.0977 9.506C2.5569 5.6516 -1.8798 4.9805 -2.8884 8.2981C-3.153 9.1684 -3.1321 10.2198 -2.8284 11.3137M2.8284 16.9706C3.9859 21.1405 0.9528 23.5152 -2.631 21.245C-6.2148 18.9748 -7.6616 13.7624 -5.2352 11.8626C-4.5986 11.3642 -3.7596 11.1729 -2.8284 11.3137');
163
+
164
+ // @ts-expect-error
165
+ const path3 = pathToString(transformPath(reversePath(simpleShapes.initial[3]), { rotate: -45 }));
166
+ expect(path3).to.equal('M16.9706 -2.8284C16.6064 -0.4208 14.1423 2.0433 11.3137 2.8284M16.9706 -2.8284C14.1419 -2.0433 11.6779 0.4208 11.3137 2.8284M16.9706 -2.8284C17.5074 -6.3778 13.3604 -7.6385 9.506 -5.0977C5.6516 -2.5569 4.9805 1.8798 8.2981 2.8884C9.1684 3.153 10.2198 3.1321 11.3137 2.8284M16.9706 -2.8284C21.1405 -3.9859 23.5152 -0.9528 21.245 2.631C18.9748 6.2148 13.7624 7.6616 11.8626 5.2352C11.3642 4.5986 11.1729 3.7596 11.3137 2.8284');
167
+ });
168
+
169
+ it(`Can do getPropertiesAtLength`, () => {
170
+ try {
171
+ SVGPathCommander.getPropertiesAtLength('M16.9706 -2.8284A4 6 89.5025 0 1 11.3137 2.8284M', 50);
172
+ } catch (er) {
173
+ expect(er).to.be.instanceOf(TypeError);
174
+ expect(er).to.have.property('message', `${error}: ${invalidPathValue} at index 48, "pathValue" is missing param`);
175
+ }
176
+
177
+ const props0 = SVGPathCommander.getPropertiesAtLength(simpleShapes.initial[0], 0);
178
+ // cy.log(props50)
179
+ expect(props0.index).to.equal(0)
180
+ expect(props0.lengthAtSegment).to.equal(0)
181
+ expect(props0.segment).to.deep.equal(["M", 10, 10])
182
+
183
+ const props50 = SVGPathCommander.getPropertiesAtLength(simpleShapes.initial[0], 50);
184
+ // cy.log(props50)
185
+ expect(props50.index).to.equal(1)
186
+ expect(props50.lengthAtSegment).to.equal(0)
187
+ expect(props50.segment).to.deep.equal(['l', 80, 80])
188
+
189
+ const props400 = SVGPathCommander.getPropertiesAtLength(simpleShapes.initial[0], 400);
190
+ // cy.log(props50)
191
+ expect(props400.index).to.equal(3)
192
+ expect(props400.lengthAtSegment).to.equal(193.1370849898476)
193
+ expect(props400.segment).to.deep.equal(['H', 50])
194
+ });
195
+
196
+ it(`Can do getPropertiesAtPoint`, () => {
197
+ const { getPropertiesAtPoint } = SVGPathCommander;
198
+
199
+ // getPropertiesAtPoint first point
200
+ const propsPoint0 = getPropertiesAtPoint(simpleShapes.initial[1], { "x": 10, "y": 90 });
201
+ expect(propsPoint0.closest).to.deep.equal({ x: 10, y: 90 });
202
+ 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 })
204
+
205
+ // getPropertiesAtPoint mid point
206
+ const propsPoint50 = getPropertiesAtPoint(simpleShapes.initial[1], { x: 30.072453006153214, y: 41.42818552481854 });
207
+ expect(propsPoint50.closest).to.deep.equal({ x: 30.072383912322863, y: 41.42816186159437 })
208
+ expect(propsPoint50.distance).to.equal(0.00007303359207048124)
209
+ expect(propsPoint50.segment).to.deep.equal({ segment: ['C', 30, 90, 25, 10, 50, 10], index: 1, length: 94.75724347727943, lengthAtSegment: 0 })
210
+
211
+ // getPropertiesAtPoint last point
212
+ const propsPoint400 = getPropertiesAtPoint(simpleShapes.initial[1], { "x": 50, "y": 10 });
213
+ expect(propsPoint400.closest).to.deep.equal({ x: 50.000003520199236, y: 10.000000000000531 })
214
+ 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})
216
+ });
217
+
218
+ it(`Can do getSegmentAtLength`, () => {
219
+ const { getSegmentAtLength } = SVGPathCommander;
220
+ expect(getSegmentAtLength(simpleShapes.initial[1])).to.deep.equal(['M', 10, 90]);
221
+ expect(getSegmentAtLength(simpleShapes.initial[1], 0)).to.deep.equal(['M', 10, 90]);
222
+ expect(getSegmentAtLength(simpleShapes.initial[3], 15)).to.deep.equal(['a', 6, 4, 10, 1, 0, 8, 0]);
223
+ expect(getSegmentAtLength(simpleShapes.initial[3], 400)).to.deep.equal(['a', 6, 4, 10, 0, 0, 8, 0]);
224
+ });
225
+
226
+ it(`Can do getSegmentOfPoint`, () => {
227
+ const { getSegmentOfPoint } = SVGPathCommander;
228
+ // 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 });
230
+ // 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 });
232
+ });
233
+
234
+ it(`Can do getClosestPoint`, () => {
235
+ const { getClosestPoint } = SVGPathCommander;
236
+ // first point
237
+ expect(getClosestPoint(simpleShapes.initial[1], { x: 10, y: 90 })).to.deep.equal({ x: 10, y: 90 });
238
+ // mid point
239
+ expect(getClosestPoint(simpleShapes.initial[3], { x: 9, y: 9 })).to.deep.equal({ x: 8.648537607602185, y: 10.940998877338636 });
240
+ });
241
+
242
+ it(`Can do isPointInStroke`, () => {
243
+ const { isPointInStroke } = SVGPathCommander;
244
+ // first point
245
+ expect(isPointInStroke(simpleShapes.initial[1], { x: 10, y: 90 })).to.be.true;
246
+ // mid point'
247
+ // 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;
249
+ // ({ x: 10, y: 10 })
250
+ expect(isPointInStroke(simpleShapes.initial[1], { x: 10, y: 10 })).to.be.false;
251
+ // ({ x: 45.355339, y: 45.355339 })
252
+ expect(isPointInStroke(simpleShapes.initial[1], { x: 45.355339, y: 45.355339 })).to.be.false;
253
+ // ({ x: 50, y: 10 })
254
+ expect(isPointInStroke(simpleShapes.initial[1], { x: 50, y: 10 })).to.be.true;
255
+ });
256
+
257
+ it(`Can do getDrawDirection`, () => {
258
+ const { getDrawDirection } = SVGPathCommander;
259
+ expect(getDrawDirection(simpleShapes.reversed[1])).to.be.true;
260
+ expect(getDrawDirection(simpleShapes.initial[1])).to.be.false;
261
+ });
262
+
263
+ it(`Can do splitCubic`, () => {
264
+ const { splitCubic } = SVGPathCommander;
265
+ expect(splitCubic([70, 60, 70, 80, 110, 80, 110, 60])).to.deep.equal([
266
+ ['C', 70, 70, 80, 75, 90, 75],
267
+ ['C', 100, 75, 110, 70, 110, 60],
268
+ ]);
269
+ });
270
+
271
+ it(`Can do polygonLength`, () => {
272
+ const { polygonLength } = SVGPathCommander;
273
+ expect(polygonLength([[100, 100], [150, 25], [150, 75], [200, 0]])).to.equal(230.27756377319946);
274
+ });
275
+
276
+ 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);
279
+ });
280
+
281
+ it(`Can do transformPath with empty object`, () => {
282
+ const { transformPath, pathToString } = SVGPathCommander;
283
+ const path = pathToString(transformPath(simpleShapes.normalized[0] as string));
284
+ expect(path).to.equal(simpleShapes.normalized[0]);
285
+ const path1 = pathToString(transformPath(simpleShapes.normalized[0], {}));
286
+ expect(path1).to.equal(simpleShapes.normalized[0]);
287
+ const path2 = pathToString(transformPath(simpleShapes.normalized[0], { origin: [0, 0, 0] }));
288
+ expect(path2).to.equal(simpleShapes.normalized[0]);
289
+ });
290
+
291
+ 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);
295
+ 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);
303
+ });
304
+ });
package/tsconfig.json CHANGED
@@ -2,13 +2,13 @@
2
2
  // https://janessagarrow.com/blog/typescript-and-esbuild/
3
3
  "compilerOptions": {
4
4
  "lib": ["DOM", "ESNext", "DOM.Iterable"],
5
- // "types": ["vite", "vite/client", "cypress", "@thednp/dommatrix"],
6
- "types": ["@thednp/dommatrix"],
5
+ // "types": ["vite", "vite/client", "vitest", "node", "@thednp/dommatrix"],
6
+ // "types": ["@thednp/dommatrix"],
7
7
  "rootDir": "./",
8
8
  "baseUrl": "./",
9
9
  "module": "ESNext",
10
10
  "target": "ESNext",
11
- "moduleResolution": "Node",
11
+ "moduleResolution": "Bundler",
12
12
  "allowJs": true,
13
13
  "forceConsistentCasingInFileNames": true,
14
14
  "useDefineForClassFields": true,
@@ -23,7 +23,12 @@
23
23
  "removeComments": false,
24
24
  "allowSyntheticDefaultImports": true,
25
25
  "noEmit": true,
26
+ "paths": {
27
+ "~/*": [
28
+ "src"
29
+ ],
30
+ }
26
31
  },
27
- "include": ["src/*", "src/**/*"],
32
+ "include": ["src/*", "test/**/*"],
28
33
  "exclude": ["node_modules", "experiments", "coverage", "dist"],
29
34
  }
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- import {resolve} from 'path';
2
+ import { resolve } from 'path';
3
3
  import { defineConfig } from 'vite';
4
4
  import { name } from './package.json';
5
5
 
@@ -17,6 +17,11 @@ const fileName = {
17
17
 
18
18
  export default defineConfig({
19
19
  base: './',
20
+ resolve: {
21
+ alias: {
22
+ "~": resolve(__dirname, "src"),
23
+ },
24
+ },
20
25
  build: {
21
26
  emptyOutDir: true,
22
27
  outDir: 'dist',
@@ -27,6 +32,10 @@ export default defineConfig({
27
32
  fileName: (format: string) => fileName[format],
28
33
  },
29
34
  sourcemap: true,
35
+ reportCompressedSize: true,
36
+ },
37
+ esbuild: {
38
+ legalComments: 'none'
30
39
  }
31
40
  });
32
41
 
@@ -0,0 +1,26 @@
1
+ import { defineConfig } from "vitest/config";
2
+ import { resolve } from 'node:path';
3
+
4
+ export default defineConfig({
5
+ resolve: {
6
+ alias: {
7
+ "~": resolve(__dirname, "src"),
8
+ },
9
+ },
10
+ test: {
11
+ css: true,
12
+ globals: true,
13
+ coverage: {
14
+ provider: "istanbul",
15
+ reporter: ["html", "text", "lcov"],
16
+ enabled: true,
17
+ include: ["src/**/*.{ts,tsx}"],
18
+ },
19
+ browser: {
20
+ // provider: 'webdriverio', // or 'webdriverio'
21
+ enabled: true,
22
+ headless: false,
23
+ name: 'chromium', // browser name is required
24
+ },
25
+ },
26
+ });
@@ -0,0 +1,26 @@
1
+ import { defineConfig } from "vitest/config";
2
+ import { resolve } from 'node:path';
3
+
4
+ export default defineConfig({
5
+ resolve: {
6
+ alias: {
7
+ "~": resolve(__dirname, "src"),
8
+ },
9
+ },
10
+ test: {
11
+ css: true,
12
+ globals: true,
13
+ coverage: {
14
+ provider: "istanbul",
15
+ reporter: ["html", "text", "lcov"],
16
+ enabled: true,
17
+ include: ["src/**/*.{ts,tsx}"],
18
+ },
19
+ browser: {
20
+ provider: 'playwright', // or 'webdriverio'
21
+ enabled: true,
22
+ headless: true,
23
+ name: 'chromium', // browser name is required
24
+ },
25
+ },
26
+ });