@js-draw/math 1.21.3 → 1.23.1
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/build-config.json +1 -1
- package/dist/cjs/Color4.d.ts +24 -1
- package/dist/cjs/Color4.js +35 -3
- package/dist/cjs/Mat33.d.ts +21 -11
- package/dist/cjs/Mat33.js +28 -24
- package/dist/cjs/Vec3.d.ts +12 -3
- package/dist/cjs/Vec3.js +20 -9
- package/dist/cjs/lib.d.ts +3 -0
- package/dist/cjs/lib.js +3 -0
- package/dist/cjs/shapes/BezierJSWrapper.d.ts +2 -0
- package/dist/cjs/shapes/BezierJSWrapper.js +22 -13
- package/dist/cjs/shapes/LineSegment2.js +13 -17
- package/dist/cjs/shapes/Parameterized2DShape.js +1 -1
- package/dist/cjs/shapes/Path.d.ts +1 -0
- package/dist/cjs/shapes/Path.js +50 -47
- package/dist/cjs/shapes/QuadraticBezier.d.ts +19 -2
- package/dist/cjs/shapes/QuadraticBezier.js +26 -3
- package/dist/cjs/shapes/Rect2.d.ts +13 -0
- package/dist/cjs/shapes/Rect2.js +35 -16
- package/dist/cjs/shapes/Triangle.js +4 -5
- package/dist/cjs/utils/convexHull2Of.js +3 -3
- package/dist/mjs/Color4.d.ts +24 -1
- package/dist/mjs/Color4.mjs +35 -3
- package/dist/mjs/Mat33.d.ts +21 -11
- package/dist/mjs/Mat33.mjs +28 -24
- package/dist/mjs/Vec3.d.ts +12 -3
- package/dist/mjs/Vec3.mjs +20 -9
- package/dist/mjs/lib.d.ts +3 -0
- package/dist/mjs/lib.mjs +3 -0
- package/dist/mjs/shapes/BezierJSWrapper.d.ts +2 -0
- package/dist/mjs/shapes/BezierJSWrapper.mjs +22 -13
- package/dist/mjs/shapes/LineSegment2.mjs +13 -17
- package/dist/mjs/shapes/Parameterized2DShape.mjs +1 -1
- package/dist/mjs/shapes/Path.d.ts +1 -0
- package/dist/mjs/shapes/Path.mjs +50 -47
- package/dist/mjs/shapes/QuadraticBezier.d.ts +19 -2
- package/dist/mjs/shapes/QuadraticBezier.mjs +26 -3
- package/dist/mjs/shapes/Rect2.d.ts +13 -0
- package/dist/mjs/shapes/Rect2.mjs +35 -16
- package/dist/mjs/shapes/Triangle.mjs +4 -5
- package/dist/mjs/utils/convexHull2Of.mjs +3 -3
- package/dist-test/test_imports/test-require.cjs +1 -1
- package/package.json +3 -3
- package/src/Color4.test.ts +21 -21
- package/src/Color4.ts +61 -18
- package/src/Mat33.fromCSSMatrix.test.ts +32 -46
- package/src/Mat33.test.ts +64 -102
- package/src/Mat33.ts +81 -104
- package/src/Vec2.test.ts +3 -3
- package/src/Vec3.test.ts +2 -3
- package/src/Vec3.ts +46 -61
- package/src/lib.ts +3 -2
- package/src/polynomial/solveQuadratic.test.ts +39 -13
- package/src/polynomial/solveQuadratic.ts +5 -6
- package/src/rounding/cleanUpNumber.test.ts +1 -1
- package/src/rounding/constants.ts +1 -3
- package/src/rounding/getLenAfterDecimal.ts +1 -2
- package/src/rounding/lib.ts +1 -2
- package/src/rounding/toRoundedString.test.ts +1 -1
- package/src/rounding/toStringOfSamePrecision.test.ts +1 -2
- package/src/rounding/toStringOfSamePrecision.ts +1 -1
- package/src/shapes/BezierJSWrapper.ts +56 -37
- package/src/shapes/CubicBezier.ts +3 -3
- package/src/shapes/LineSegment2.test.ts +24 -17
- package/src/shapes/LineSegment2.ts +26 -29
- package/src/shapes/Parameterized2DShape.ts +5 -4
- package/src/shapes/Path.fromString.test.ts +5 -5
- package/src/shapes/Path.test.ts +122 -120
- package/src/shapes/Path.toString.test.ts +7 -7
- package/src/shapes/Path.ts +379 -352
- package/src/shapes/PointShape2D.ts +3 -3
- package/src/shapes/QuadraticBezier.test.ts +27 -21
- package/src/shapes/QuadraticBezier.ts +26 -11
- package/src/shapes/Rect2.test.ts +44 -75
- package/src/shapes/Rect2.ts +47 -35
- package/src/shapes/Triangle.test.ts +31 -29
- package/src/shapes/Triangle.ts +17 -18
- package/src/utils/convexHull2Of.test.ts +54 -15
- package/src/utils/convexHull2Of.ts +9 -7
- package/tsconfig.json +1 -3
- package/typedoc.json +2 -2
package/src/shapes/Path.test.ts
CHANGED
@@ -35,10 +35,15 @@ describe('Path', () => {
|
|
35
35
|
|
36
36
|
// Make sure the control points (and start/end points) match what was set
|
37
37
|
expect(firstItem.getPoints()).toMatchObject([
|
38
|
-
{ x: 0, y: 0 },
|
38
|
+
{ x: 0, y: 0 },
|
39
|
+
{ x: 1, y: 1 },
|
40
|
+
{ x: -1, y: -1 },
|
41
|
+
{ x: 3, y: 3 },
|
39
42
|
]);
|
40
43
|
expect(secondItem.getPoints()).toMatchObject([
|
41
|
-
{ x: 3, y: 3 },
|
44
|
+
{ x: 3, y: 3 },
|
45
|
+
{ x: 1, y: 1 },
|
46
|
+
{ x: 0, y: 0 },
|
42
47
|
]);
|
43
48
|
});
|
44
49
|
|
@@ -55,23 +60,21 @@ describe('Path', () => {
|
|
55
60
|
|
56
61
|
expect(path.geometry.length).toBe(1);
|
57
62
|
expect(path.geometry[0]).toBeInstanceOf(LineSegment2);
|
58
|
-
expect(path.geometry[0]).toMatchObject(
|
59
|
-
new LineSegment2(lineStart, lineEnd)
|
60
|
-
);
|
63
|
+
expect(path.geometry[0]).toMatchObject(new LineSegment2(lineStart, lineEnd));
|
61
64
|
});
|
62
65
|
|
63
66
|
it.each([
|
64
|
-
[
|
65
|
-
[
|
66
|
-
[
|
67
|
-
[
|
68
|
-
[
|
69
|
-
[
|
70
|
-
[
|
71
|
-
[
|
72
|
-
[
|
73
|
-
[
|
74
|
-
[
|
67
|
+
['m0,0 L1,1', 'M0,0 L1,1', true],
|
68
|
+
['m0,0 L1,1', 'M1,1 L0,0', false],
|
69
|
+
['m0,0 L1,1 Q2,3 4,5', 'M1,1 L0,0', false],
|
70
|
+
['m0,0 L1,1 Q2,3 4,5', 'M1,1 L0,0 Q2,3 4,5', false],
|
71
|
+
['m0,0 L1,1 Q2,3 4,5', 'M0,0 L1,1 Q2,3 4,5', true],
|
72
|
+
['m0,0 L1,1 Q2,3 4,5 C4,5 6,7 8,9', 'M0,0 L1,1 Q2,3 4,5 C4,5 6,7 8,9', true],
|
73
|
+
['m0,0 L1,1 Q2,3 4,5 C4,5 6,7 8,9Z', 'M0,0 L1,1 Q2,3 4,5 C4,5 6,7 8,9', false],
|
74
|
+
['m0,0 L1,1 Q2,3 4,5 C4,5 6,7 8,9', 'M0,0 L1,1 Q2,3 4,5 C4,5 6,7 8,9Z', false],
|
75
|
+
['m0,0 L1,1 Q2,3 4,5 C4,5 6,7 8,9', 'M0,0 L1,1 Q2,3 4,5 C4,5 6,7 8,9.01', false],
|
76
|
+
['m0,0 L1,1 Q2,3 4,5 C4,5 6,7 8,9', 'M0,0 L1,1 Q2,3 4,5 C4,5 6,7.01 8,9', false],
|
77
|
+
['m0,0 L1,1 Q2,3 4,5 C4,5 6,7 8,9', 'M0,0 L1,1 Q2,3 4,5 C4,5.01 6,7 8,9', false],
|
75
78
|
])('.eq should check equality', (path1Str, path2Str, shouldEqual) => {
|
76
79
|
expect(Path.fromString(path1Str)).objEq(Path.fromString(path1Str));
|
77
80
|
expect(Path.fromString(path2Str)).objEq(Path.fromString(path2Str));
|
@@ -97,7 +100,7 @@ describe('Path', () => {
|
|
97
100
|
]);
|
98
101
|
|
99
102
|
const intersections = path.intersection(
|
100
|
-
new LineSegment2(Vec2.of(-50, 200), Vec2.of(-50, -200))
|
103
|
+
new LineSegment2(Vec2.of(-50, 200), Vec2.of(-50, -200)),
|
101
104
|
);
|
102
105
|
|
103
106
|
// Should only have intersections in quadrants II and III.
|
@@ -143,7 +146,8 @@ describe('Path', () => {
|
|
143
146
|
|
144
147
|
const strokeWidth = 5;
|
145
148
|
let intersections = path.intersection(
|
146
|
-
new LineSegment2(Vec2.of(2000, 200), Vec2.of(2000, 400)),
|
149
|
+
new LineSegment2(Vec2.of(2000, 200), Vec2.of(2000, 400)),
|
150
|
+
strokeWidth,
|
147
151
|
);
|
148
152
|
expect(intersections.length).toBe(0);
|
149
153
|
|
@@ -158,7 +162,8 @@ describe('Path', () => {
|
|
158
162
|
// ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
|
159
163
|
|
160
164
|
intersections = path.intersection(
|
161
|
-
new LineSegment2(Vec2.of(-50, 200), Vec2.of(-50, 100)),
|
165
|
+
new LineSegment2(Vec2.of(-50, 200), Vec2.of(-50, 100)),
|
166
|
+
strokeWidth,
|
162
167
|
);
|
163
168
|
expect(intersections.length).toBe(1);
|
164
169
|
expect(intersections[0].point.xy).toMatchObject({
|
@@ -166,10 +171,10 @@ describe('Path', () => {
|
|
166
171
|
y: 105,
|
167
172
|
});
|
168
173
|
|
169
|
-
|
170
174
|
// Changing the order of the end points on the line should not change the result
|
171
175
|
intersections = path.intersection(
|
172
|
-
new LineSegment2(Vec2.of(-50, 100), Vec2.of(-50, 200)),
|
176
|
+
new LineSegment2(Vec2.of(-50, 100), Vec2.of(-50, 200)),
|
177
|
+
strokeWidth,
|
173
178
|
);
|
174
179
|
expect(intersections.length).toBe(1);
|
175
180
|
expect(intersections[0].point.xy).toMatchObject({
|
@@ -181,7 +186,8 @@ describe('Path', () => {
|
|
181
186
|
// intersections — one entering and one leaving for each intersection with the
|
182
187
|
// centers.
|
183
188
|
intersections = path.intersection(
|
184
|
-
new LineSegment2(Vec2.of(-50, 200), Vec2.of(-50, -200)),
|
189
|
+
new LineSegment2(Vec2.of(-50, 200), Vec2.of(-50, -200)),
|
190
|
+
strokeWidth,
|
185
191
|
);
|
186
192
|
expect(intersections.length).toBe(4);
|
187
193
|
|
@@ -204,7 +210,7 @@ describe('Path', () => {
|
|
204
210
|
kind: PathCommandType.QuadraticBezierTo,
|
205
211
|
controlPoint: Vec2.unitX,
|
206
212
|
endPoint: Vec2.unitY,
|
207
|
-
}
|
213
|
+
},
|
208
214
|
]);
|
209
215
|
|
210
216
|
const strokeWidth = 5;
|
@@ -212,13 +218,15 @@ describe('Path', () => {
|
|
212
218
|
// Should be no intersections for a line contained entirely within the stroke
|
213
219
|
// (including stroke width).
|
214
220
|
let intersections = path.intersection(
|
215
|
-
new LineSegment2(Vec2.of(-1, 0.5), Vec2.of(2, 0.5)),
|
221
|
+
new LineSegment2(Vec2.of(-1, 0.5), Vec2.of(2, 0.5)),
|
222
|
+
strokeWidth,
|
216
223
|
);
|
217
224
|
expect(intersections).toHaveLength(0);
|
218
225
|
|
219
226
|
// Should be an intersection when exiting/entering the edge of the stroke
|
220
227
|
intersections = path.intersection(
|
221
|
-
new LineSegment2(Vec2.of(0, 0.5), Vec2.of(8, 0.5)),
|
228
|
+
new LineSegment2(Vec2.of(0, 0.5), Vec2.of(8, 0.5)),
|
229
|
+
strokeWidth,
|
222
230
|
);
|
223
231
|
expect(intersections).toHaveLength(1);
|
224
232
|
});
|
@@ -226,10 +234,7 @@ describe('Path', () => {
|
|
226
234
|
it('should correctly report intersections near the cap of a line-like Bézier', () => {
|
227
235
|
const path = Path.fromString('M0,0Q14,0 27,0');
|
228
236
|
expect(
|
229
|
-
path.intersection(
|
230
|
-
new LineSegment2(Vec2.of(0, -100), Vec2.of(0, 100)),
|
231
|
-
10,
|
232
|
-
),
|
237
|
+
path.intersection(new LineSegment2(Vec2.of(0, -100), Vec2.of(0, 100)), 10),
|
233
238
|
|
234
239
|
// Should have intersections, despite being at the cap of the Bézier
|
235
240
|
// curve.
|
@@ -237,22 +242,31 @@ describe('Path', () => {
|
|
237
242
|
});
|
238
243
|
|
239
244
|
it.each([
|
240
|
-
[new LineSegment2(Vec2.of(43.5
|
241
|
-
[new LineSegment2(Vec2.of(35.5,19.5), Vec2.of(38.5
|
242
|
-
])(
|
243
|
-
|
244
|
-
|
245
|
-
|
245
|
+
[new LineSegment2(Vec2.of(43.5, -12.5), Vec2.of(40.5, 24.5)), 0],
|
246
|
+
[new LineSegment2(Vec2.of(35.5, 19.5), Vec2.of(38.5, -17.5)), 0],
|
247
|
+
])(
|
248
|
+
'should correctly report positive intersections with a line-like Bézier',
|
249
|
+
(line, strokeRadius) => {
|
250
|
+
const bezier = Path.fromString('M0,0 Q50,0 100,0');
|
251
|
+
expect(bezier.intersection(line, strokeRadius).length).toBeGreaterThan(0);
|
252
|
+
},
|
253
|
+
);
|
246
254
|
|
247
255
|
it('should handle near-vertical lines', () => {
|
248
|
-
const intersections = Path.fromString('M0,0 Q50,0 100,0').intersection(
|
256
|
+
const intersections = Path.fromString('M0,0 Q50,0 100,0').intersection(
|
257
|
+
new LineSegment2(Vec2.of(44, -12), Vec2.of(39, 25)),
|
258
|
+
);
|
249
259
|
expect(intersections).toHaveLength(1);
|
250
260
|
});
|
251
261
|
|
252
262
|
it('should handle single-point strokes', () => {
|
253
263
|
const stroke = new Path(Vec2.zero, []);
|
254
|
-
expect(
|
255
|
-
|
264
|
+
expect(
|
265
|
+
stroke.intersection(new LineSegment2(Vec2.of(-2, -20), Vec2.of(-2, -1)), 1),
|
266
|
+
).toHaveLength(0);
|
267
|
+
expect(stroke.intersection(new LineSegment2(Vec2.of(-2, -2), Vec2.of(2, 2)), 1)).toHaveLength(
|
268
|
+
2,
|
269
|
+
);
|
256
270
|
});
|
257
271
|
});
|
258
272
|
|
@@ -272,18 +286,12 @@ describe('Path', () => {
|
|
272
286
|
describe('roughlyIntersectsClosed', () => {
|
273
287
|
it('small, line-only path', () => {
|
274
288
|
const path = Path.fromString('m0,0 l10,10 L0,10 z');
|
275
|
-
expect(
|
276
|
-
|
277
|
-
)
|
278
|
-
expect(
|
279
|
-
|
280
|
-
).toBe(true);
|
281
|
-
expect(
|
282
|
-
path.closedRoughlyIntersects(new Rect2(10, 1, 1, 1))
|
283
|
-
).toBe(false);
|
284
|
-
expect(
|
285
|
-
path.closedRoughlyIntersects(new Rect2(1, 5, 1, 1))
|
286
|
-
).toBe(true);
|
289
|
+
expect(path.closedRoughlyIntersects(Rect2.fromCorners(Vec2.zero, Vec2.of(20, 20)))).toBe(
|
290
|
+
true,
|
291
|
+
);
|
292
|
+
expect(path.closedRoughlyIntersects(Rect2.fromCorners(Vec2.zero, Vec2.of(2, 2)))).toBe(true);
|
293
|
+
expect(path.closedRoughlyIntersects(new Rect2(10, 1, 1, 1))).toBe(false);
|
294
|
+
expect(path.closedRoughlyIntersects(new Rect2(1, 5, 1, 1))).toBe(true);
|
287
295
|
});
|
288
296
|
|
289
297
|
it('path with Bézier curves', () => {
|
@@ -296,15 +304,9 @@ describe('Path', () => {
|
|
296
304
|
Q670,470 960,980
|
297
305
|
Q1230,1370 1090,2560
|
298
306
|
`);
|
299
|
-
expect(
|
300
|
-
|
301
|
-
).toBe(
|
302
|
-
expect(
|
303
|
-
path.closedRoughlyIntersects(new Rect2(0, 0, 5, 5))
|
304
|
-
).toBe(true);
|
305
|
-
expect(
|
306
|
-
path.closedRoughlyIntersects(new Rect2(-10000, 0, 500, 500))
|
307
|
-
).toBe(false);
|
307
|
+
expect(path.closedRoughlyIntersects(new Rect2(0, 0, 500, 500))).toBe(true);
|
308
|
+
expect(path.closedRoughlyIntersects(new Rect2(0, 0, 5, 5))).toBe(true);
|
309
|
+
expect(path.closedRoughlyIntersects(new Rect2(-10000, 0, 500, 500))).toBe(false);
|
308
310
|
});
|
309
311
|
});
|
310
312
|
|
@@ -315,21 +317,11 @@ describe('Path', () => {
|
|
315
317
|
L20,20
|
316
318
|
L100,21
|
317
319
|
`);
|
318
|
-
expect(
|
319
|
-
|
320
|
-
).toBe(
|
321
|
-
expect(
|
322
|
-
|
323
|
-
).toBe(false);
|
324
|
-
expect(
|
325
|
-
path.roughlyIntersects(new Rect2(8, 22, 1, 1))
|
326
|
-
).toBe(false);
|
327
|
-
expect(
|
328
|
-
path.roughlyIntersects(new Rect2(21, 11, 1, 1))
|
329
|
-
).toBe(false);
|
330
|
-
expect(
|
331
|
-
path.roughlyIntersects(new Rect2(50, 19, 1, 2))
|
332
|
-
).toBe(true);
|
320
|
+
expect(path.roughlyIntersects(new Rect2(0, 0, 50, 50))).toBe(true);
|
321
|
+
expect(path.roughlyIntersects(new Rect2(0, 0, 5, 5))).toBe(false);
|
322
|
+
expect(path.roughlyIntersects(new Rect2(8, 22, 1, 1))).toBe(false);
|
323
|
+
expect(path.roughlyIntersects(new Rect2(21, 11, 1, 1))).toBe(false);
|
324
|
+
expect(path.roughlyIntersects(new Rect2(50, 19, 1, 2))).toBe(true);
|
333
325
|
});
|
334
326
|
});
|
335
327
|
|
@@ -358,9 +350,7 @@ describe('Path', () => {
|
|
358
350
|
});
|
359
351
|
|
360
352
|
describe('splitAt', () => {
|
361
|
-
it.each([
|
362
|
-
2, 3, 4, 5,
|
363
|
-
])('should split a line into %d sections', (numSections) => {
|
353
|
+
it.each([2, 3, 4, 5])('should split a line into %d sections', (numSections) => {
|
364
354
|
const path = Path.fromString('m0,0 l1,0');
|
365
355
|
|
366
356
|
const splitIndices: CurveIndexRecord[] = [];
|
@@ -371,7 +361,7 @@ describe('Path', () => {
|
|
371
361
|
|
372
362
|
expect(split).toHaveLength(numSections + 1);
|
373
363
|
expect(split[numSections].getEndPoint()).objEq(Vec2.unitX);
|
374
|
-
for (let i = 0; i < numSections; i
|
364
|
+
for (let i = 0; i < numSections; i++) {
|
375
365
|
expect(split[i].geometry).toHaveLength(1);
|
376
366
|
const geom = split[i].geometry[0] as LineSegment2;
|
377
367
|
expect(geom.p1.y).toBeCloseTo(0);
|
@@ -432,13 +422,13 @@ describe('Path', () => {
|
|
432
422
|
original: 'm0,0 Q4,0 8,0 Q8,4 8,8',
|
433
423
|
near: Vec2.of(8, 4),
|
434
424
|
map: (p: Point2) => p.plus(Vec2.of(1, 1)),
|
435
|
-
expected: [
|
425
|
+
expected: ['M0,0Q4,0 8,0Q9,3 9,5', 'M9,5Q9,7 9,9'],
|
436
426
|
},
|
437
427
|
{
|
438
428
|
original: 'm0,0 L0,10',
|
439
429
|
near: Vec2.of(0, 5),
|
440
430
|
map: (p: Point2) => p.plus(Vec2.of(100, 0)),
|
441
|
-
expected: [
|
431
|
+
expected: ['M0,0L100,5', 'M100,5L0,10'],
|
442
432
|
},
|
443
433
|
{
|
444
434
|
// Tested using SVG data similar to:
|
@@ -448,13 +438,16 @@ describe('Path', () => {
|
|
448
438
|
original: 'm1,1 C1,2 2,10 4,4 C5,0 9,3 7,7',
|
449
439
|
near: Vec2.of(3, 5),
|
450
440
|
map: (p: Point2) => Vec2.of(Math.round(p.x), Math.round(p.y)),
|
451
|
-
expected: [
|
441
|
+
expected: ['M1,1C1,2 1,6 2,6', 'M2,6C3,6 3,6 4,4C5,0 9,3 7,7'],
|
452
442
|
},
|
453
|
-
])(
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
443
|
+
])(
|
444
|
+
'should support mapping newly-added points while splitting (case %j)',
|
445
|
+
({ original, near, map, expected }) => {
|
446
|
+
const path = Path.fromString(original);
|
447
|
+
const split = path.splitNear(near, { mapNewPoint: map });
|
448
|
+
expect(split.map((p) => p.toString(false))).toMatchObject(expected);
|
449
|
+
},
|
450
|
+
);
|
458
451
|
});
|
459
452
|
|
460
453
|
describe('spliced', () => {
|
@@ -485,23 +478,26 @@ describe('Path', () => {
|
|
485
478
|
insert: 'M1,0 L1,1 L3,1 L3,0',
|
486
479
|
expected: 'M1,0 L3,0 L3,1 L1,1 L1,0',
|
487
480
|
},
|
488
|
-
])(
|
489
|
-
|
490
|
-
|
491
|
-
originalCurve.
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
481
|
+
])(
|
482
|
+
'.spliced should support inserting paths inbetween other paths (case %#)',
|
483
|
+
({ curve, from, to, insert, expected }) => {
|
484
|
+
const originalCurve = Path.fromString(curve);
|
485
|
+
expect(
|
486
|
+
originalCurve.spliced(
|
487
|
+
{ curveIndex: from.i, parameterValue: from.t },
|
488
|
+
{ curveIndex: to.i, parameterValue: to.t },
|
489
|
+
Path.fromString(insert),
|
490
|
+
),
|
491
|
+
).objEq(Path.fromString(expected));
|
492
|
+
},
|
493
|
+
);
|
498
494
|
});
|
499
495
|
|
500
496
|
it.each([
|
501
|
-
[
|
502
|
-
[
|
503
|
-
[
|
504
|
-
[
|
497
|
+
['m0,0 L1,1', 'M1,1 L0,0'],
|
498
|
+
['m0,0 L1,1', 'M1,1 L0,0'],
|
499
|
+
['M0,0 L1,1 Q2,2 3,3', 'M3,3 Q2,2 1,1 L0,0'],
|
500
|
+
['M0,0 L1,1 Q4,2 5,3 C12,13 10,9 8,7', 'M8,7 C 10,9 12,13 5,3 Q 4,2 1,1 L 0,0'],
|
505
501
|
])('.reversed should reverse paths', (original, expected) => {
|
506
502
|
expect(Path.fromString(original).reversed()).objEq(Path.fromString(expected));
|
507
503
|
expect(Path.fromString(expected).reversed()).objEq(Path.fromString(original));
|
@@ -509,29 +505,35 @@ describe('Path', () => {
|
|
509
505
|
});
|
510
506
|
|
511
507
|
it.each([
|
512
|
-
[
|
513
|
-
[
|
514
|
-
[
|
515
|
-
])(
|
516
|
-
|
517
|
-
|
508
|
+
['m0,0 l1,0', Vec2.of(0, 0), Vec2.of(0, 0)],
|
509
|
+
['m0,0 l1,0', Vec2.of(0.5, 0), Vec2.of(0.5, 0)],
|
510
|
+
['m0,0 Q1,0 1,2', Vec2.of(1, 0), Vec2.of(0.6236, 0.299)],
|
511
|
+
])(
|
512
|
+
'.nearestPointTo should return the closest point on a path to the given parameter (case %#)',
|
513
|
+
(path, point, expectedClosest) => {
|
514
|
+
expect(Path.fromString(path).nearestPointTo(point).point).objEq(expectedClosest, 0.002);
|
515
|
+
},
|
516
|
+
);
|
518
517
|
|
519
518
|
it.each([
|
520
519
|
// Polyline
|
521
|
-
[
|
522
|
-
[
|
523
|
-
[
|
524
|
-
[
|
525
|
-
[
|
520
|
+
['m0,0 l1,0 l0,1', [0, 0.5], Vec2.of(1, 0)],
|
521
|
+
['m0,0 l1,0 l0,1', [0, 0.99], Vec2.of(1, 0)],
|
522
|
+
['m0,0 l1,0 l0,1', [1, 0], Vec2.of(0, 1)],
|
523
|
+
['m0,0 l1,0 l0,1', [1, 0.5], Vec2.of(0, 1)],
|
524
|
+
['m0,0 l1,0 l0,1', [1, 1], Vec2.of(0, 1)],
|
526
525
|
|
527
526
|
// Shape with quadratic Bézier curves
|
528
|
-
[
|
529
|
-
[
|
530
|
-
[
|
531
|
-
[
|
532
|
-
])(
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
527
|
+
['M0,0 Q1,0 0,1', [0, 0], Vec2.of(1, 0)],
|
528
|
+
['M0,0 Q1,1 0,1', [0, 1], Vec2.of(-1, 0)],
|
529
|
+
['M0,0 Q1,0 1,1 Q0,1 0,2', [0, 1], Vec2.of(0, 1)],
|
530
|
+
['M0,0 Q1,0 1,1 Q0,1 0,2', [1, 1], Vec2.of(0, 1)],
|
531
|
+
])(
|
532
|
+
'.tangentAt should point in the direction of increasing parameter values, for curve %s at %j',
|
533
|
+
(pathString, evalAt, expected) => {
|
534
|
+
const at: CurveIndexRecord = { curveIndex: evalAt[0], parameterValue: evalAt[1] };
|
535
|
+
const path = Path.fromString(pathString);
|
536
|
+
expect(path.tangentAt(at)).objEq(expected);
|
537
|
+
},
|
538
|
+
);
|
537
539
|
});
|
@@ -1,7 +1,6 @@
|
|
1
1
|
import Path, { PathCommandType } from './Path';
|
2
2
|
import { Vec2 } from '../Vec2';
|
3
3
|
|
4
|
-
|
5
4
|
describe('Path.toString', () => {
|
6
5
|
it('a single-point path should produce a move-to command', () => {
|
7
6
|
const path = new Path(Vec2.of(0, 0), []);
|
@@ -27,8 +26,8 @@ describe('Path.toString', () => {
|
|
27
26
|
},
|
28
27
|
{
|
29
28
|
kind: PathCommandType.LineTo,
|
30
|
-
point: Vec2.of(184.00482359999998, 1)
|
31
|
-
}
|
29
|
+
point: Vec2.of(184.00482359999998, 1),
|
30
|
+
},
|
32
31
|
]);
|
33
32
|
|
34
33
|
expect(path.toString()).toBe('M.1,.2Q9999,-11 .0003,1.4L184.0048236,1');
|
@@ -49,13 +48,14 @@ describe('Path.toString', () => {
|
|
49
48
|
const path1 = Path.fromString('M100,100 L101,101 Q102,102 90.000000001,89.99999999 Z');
|
50
49
|
const ignoreCache = true;
|
51
50
|
|
52
|
-
expect(path1.toString(undefined, ignoreCache)).toBe(
|
53
|
-
'M100,100', 'l1,1', 'q1,1 -11-11', 'l10,10'
|
54
|
-
|
51
|
+
expect(path1.toString(undefined, ignoreCache)).toBe(
|
52
|
+
['M100,100', 'l1,1', 'q1,1 -11-11', 'l10,10'].join(''),
|
53
|
+
);
|
55
54
|
});
|
56
55
|
|
57
56
|
it('should not lose precision when saving', () => {
|
58
|
-
const pathStr =
|
57
|
+
const pathStr =
|
58
|
+
'M184.2,52.3l-.2-.2q-2.7,2.4 -3.2,3.5q-2.8,7 -.9,6.1q4.3-2.6 4.8-6.1q1.2-8.8 .4-8.3q-4.2,5.2 -3.9,3.9q.2-1.6 .3-2.1q.2-1.3 -.2-1q-3.8,6.5 -3.2,3.3q.6-4.1 1.1-5.3q4.1-10 3.3-8.3q-5.3,13.1 -6.6,14.1q-3.3,2.8 -1.8-1.5q2.8-9.7 2.7-8.4q0,.3 0,.4q-1.4,7.1 -2.7,8.5q-2.6,3.2 -2.5,2.9q-.3-1.9 -.7-1.9q-4.1,4.4 -2.9,1.9q1.1-3 .3-2.6q-1.8,2 -2.5,2.4q-4.5,2.8 -4.2,1.9q.3-1.6 .2-1.4q1.5,2.2 1.3,2.9q-.8,3.9 -.5,3.3q.8-7.6 2.5-13.3q2.6-9.2 2.9-6.9q.3,1.4 .3,1.2q-.7-.4 -.9,0q-2.2,11.6 -7.6,13.6q-3.9,1.6 -2.1-1.3q3-5.5 2.6-3.4q-.2,1.8 -.5,1.8q-3.2,.5 -4.1,1.2q-2.6,2.6 -1.9,2.5q4.7-4.4 3.7-5.5q-1.1-.9 -1.6-.6q-7.2,7.5 -3.9,6.5q.3-.1 .4-.4q.6-5.3 -.2-4.9q-2.8,2.3 -3.1,2.4q-3.7,1.5 -3.5,.5q.3-3.6 1.4-3.3q3.5,.7 1.9,2.4q-1.7,2.3 -1.6,.8q0-3.5 -.9-3.1q-5.1,3.3 -4.9,2.8q.1-4 -.8-3.5q-4.3,3.4 -4.6,2.5q-1-2.1 .5-8.7l-.2,0q-1.6,6.6 -.7,8.9q.7,1.2 5.2-2.3q.4-.5 .2,3.1q.1,1 5.5-2.4q.4-.4 .3,2.7q.1,2 2.4-.4q1.7-2.3 -2.1-3.2q-1.7-.3 -2,3.7q0,1.4 4.1-.1q.3-.1 3.1-2.4q.3-.5 -.4,4.5q0-.1 -.2,0q-2.6,1.2 4.5-5.7q0-.2 .8,.6q.9,.6 -3.7,4.7q-.5,1 2.7-1.7q.6-.7 3.7-1.2q.7-.2 .9-2.2q.1-2.7 -3.4,3.2q-1.8,3.4 2.7,1.9q5.6-2.1 7.8-14q-.1,.1 .3,.4q.6,.1 .3-1.6q-.7-2.8 -3.7,6.7q-1.8,5.8 -2.5,13.5q.1,1.1 1.3-3.1q.2-1 -1.3-3.3q-.5-.5 -1,1.6q-.1,1.3 4.8-1.5q1-1 3-2q.1-.4 -1.1,2q-1.1,3.1 3.7-1.3q-.4,0 -.1,1.5q.3,.8 3.3-2.5q1.3-1.6 2.7-8.9q0-.1 0-.4q-.3-1.9 -3.5,8.2q-1.3,4.9 2.4,2.1q1.4-1.2 6.6-14.3q.8-2.4 -3.9,7.9q-.6,1.3 -1.1,5.5q-.3,3.7 4-3.1q-.2,0 -.6,.6q-.2,.6 -.3,2.3q0,1.8 4.7-3.5q.1-.5 -1.2,7.9q-.5,3.2 -4.6,5.7q-1.3,1 1.5-5.5q.4-1.1 3.01-3.5';
|
59
59
|
|
60
60
|
const path1 = Path.fromString(pathStr);
|
61
61
|
const ignoreCache = true;
|