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.
- package/.eslintrc.cjs +1 -0
- package/README.md +4 -4
- package/dist/svg-path-commander.cjs +1 -1
- package/dist/svg-path-commander.cjs.map +1 -1
- package/dist/svg-path-commander.d.ts +156 -39
- package/dist/svg-path-commander.js +1 -1
- package/dist/svg-path-commander.js.map +1 -1
- package/dist/svg-path-commander.mjs +869 -705
- package/dist/svg-path-commander.mjs.map +1 -1
- package/package.json +22 -24
- package/src/convert/pathToAbsolute.ts +1 -1
- package/src/convert/pathToCurve.ts +1 -1
- package/src/convert/pathToRelative.ts +1 -1
- package/src/index.ts +30 -26
- package/src/interface.ts +32 -32
- package/src/math/arcTools.ts +217 -0
- package/src/math/bezier.ts +261 -0
- package/src/math/cubicTools.ts +81 -0
- package/src/math/lineTools.ts +52 -0
- package/src/math/quadTools.ts +79 -0
- package/src/parser/isMoveCommand.ts +17 -0
- package/src/parser/parsePathString.ts +1 -1
- package/src/parser/scanSegment.ts +12 -3
- package/src/process/normalizePath.ts +1 -1
- package/src/process/replaceArc.ts +52 -0
- package/src/process/splitPath.ts +1 -1
- package/src/process/transformPath.ts +14 -34
- package/src/types.ts +5 -0
- package/src/util/distanceEpsilon.ts +3 -0
- package/src/util/getClosestPoint.ts +1 -1
- package/src/util/getPathBBox.ts +4 -3
- package/src/util/getPointAtLength.ts +3 -3
- package/src/util/getPropertiesAtLength.ts +2 -1
- package/src/util/getPropertiesAtPoint.ts +4 -1
- package/src/util/getTotalLength.ts +2 -2
- package/src/util/isPointInStroke.ts +2 -1
- package/src/util/pathFactory.ts +130 -0
- package/src/util/shapeToPathArray.ts +8 -4
- package/test/class.test.ts +501 -0
- package/test/fixtures/getMarkup.ts +17 -0
- package/{cypress → test}/fixtures/shapes.js +18 -18
- package/{cypress → test}/fixtures/simpleShapes.js +6 -6
- package/test/static.test.ts +304 -0
- package/tsconfig.json +9 -4
- package/{vite.config.ts → vite.config.mts} +10 -1
- package/vitest.config-ui.mts +26 -0
- package/vitest.config.mts +26 -0
- package/cypress/e2e/svg-path-commander.spec.ts +0 -868
- package/cypress/plugins/esbuild-istanbul.ts +0 -50
- package/cypress/plugins/tsCompile.ts +0 -34
- package/cypress/support/commands.ts +0 -37
- package/cypress/support/e2e.ts +0 -21
- package/cypress/test.html +0 -36
- package/src/util/pathLengthFactory.ts +0 -114
- package/src/util/segmentArcFactory.ts +0 -219
- package/src/util/segmentCubicFactory.ts +0 -114
- package/src/util/segmentLineFactory.ts +0 -45
- package/src/util/segmentQuadFactory.ts +0 -109
- /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", "
|
|
6
|
-
|
|
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": "
|
|
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/*", "
|
|
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
|
+
});
|