@js-draw/math 1.21.3 → 1.22.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/build-config.json +1 -1
- package/dist/cjs/Color4.js +2 -2
- package/dist/cjs/Mat33.d.ts +1 -11
- package/dist/cjs/Mat33.js +8 -24
- package/dist/cjs/Vec3.js +9 -7
- package/dist/cjs/shapes/BezierJSWrapper.js +20 -13
- package/dist/cjs/shapes/LineSegment2.js +13 -17
- package/dist/cjs/shapes/Parameterized2DShape.js +1 -1
- package/dist/cjs/shapes/Path.js +49 -47
- package/dist/cjs/shapes/Rect2.js +13 -15
- package/dist/cjs/shapes/Triangle.js +4 -5
- package/dist/cjs/utils/convexHull2Of.js +3 -3
- package/dist/mjs/Color4.mjs +2 -2
- package/dist/mjs/Mat33.d.ts +1 -11
- package/dist/mjs/Mat33.mjs +8 -24
- package/dist/mjs/Vec3.mjs +9 -7
- package/dist/mjs/shapes/BezierJSWrapper.mjs +20 -13
- package/dist/mjs/shapes/LineSegment2.mjs +13 -17
- package/dist/mjs/shapes/Parameterized2DShape.mjs +1 -1
- package/dist/mjs/shapes/Path.mjs +49 -47
- package/dist/mjs/shapes/Rect2.mjs +13 -15
- 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 +16 -21
- package/src/Color4.ts +22 -17
- package/src/Mat33.fromCSSMatrix.test.ts +31 -45
- package/src/Mat33.test.ts +58 -96
- package/src/Mat33.ts +61 -104
- package/src/Vec2.test.ts +3 -3
- package/src/Vec3.test.ts +2 -3
- package/src/Vec3.ts +34 -58
- package/src/lib.ts +0 -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 +54 -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 +378 -352
- package/src/shapes/PointShape2D.ts +3 -3
- package/src/shapes/QuadraticBezier.test.ts +27 -21
- package/src/shapes/QuadraticBezier.ts +4 -9
- package/src/shapes/Rect2.test.ts +44 -75
- package/src/shapes/Rect2.ts +30 -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/Mat33.ts
CHANGED
@@ -4,11 +4,7 @@ import Vec3 from './Vec3';
|
|
4
4
|
/**
|
5
5
|
* See {@link Mat33.toArray}.
|
6
6
|
*/
|
7
|
-
export type Mat33Array = [
|
8
|
-
number, number, number,
|
9
|
-
number, number, number,
|
10
|
-
number, number, number,
|
11
|
-
];
|
7
|
+
export type Mat33Array = [number, number, number, number, number, number, number, number, number];
|
12
8
|
|
13
9
|
/**
|
14
10
|
* Represents a three dimensional linear transformation or
|
@@ -82,13 +78,9 @@ export class Mat33 {
|
|
82
78
|
|
83
79
|
public readonly c1: number,
|
84
80
|
public readonly c2: number,
|
85
|
-
public readonly c3: number
|
81
|
+
public readonly c3: number,
|
86
82
|
) {
|
87
|
-
this.rows = [
|
88
|
-
Vec3.of(a1, a2, a3),
|
89
|
-
Vec3.of(b1, b2, b3),
|
90
|
-
Vec3.of(c1, c2, c3),
|
91
|
-
];
|
83
|
+
this.rows = [Vec3.of(a1, a2, a3), Vec3.of(b1, b2, b3), Vec3.of(c1, c2, c3)];
|
92
84
|
}
|
93
85
|
|
94
86
|
/**
|
@@ -102,19 +94,11 @@ export class Mat33 {
|
|
102
94
|
* $$
|
103
95
|
*/
|
104
96
|
public static ofRows(r1: Vec3, r2: Vec3, r3: Vec3): Mat33 {
|
105
|
-
return new Mat33(
|
106
|
-
r1.x, r1.y, r1.z,
|
107
|
-
r2.x, r2.y, r2.z,
|
108
|
-
r3.x, r3.y, r3.z
|
109
|
-
);
|
97
|
+
return new Mat33(r1.x, r1.y, r1.z, r2.x, r2.y, r2.z, r3.x, r3.y, r3.z);
|
110
98
|
}
|
111
99
|
|
112
100
|
/** The 3x3 [identity matrix](https://en.wikipedia.org/wiki/Identity_matrix). */
|
113
|
-
public static identity = new Mat33(
|
114
|
-
1, 0, 0,
|
115
|
-
0, 1, 0,
|
116
|
-
0, 0, 1
|
117
|
-
);
|
101
|
+
public static identity = new Mat33(1, 0, 0, 0, 1, 0, 0, 0, 1);
|
118
102
|
|
119
103
|
/**
|
120
104
|
* Either returns the inverse of this, or, if this matrix is singular/uninvertable,
|
@@ -131,23 +115,15 @@ export class Mat33 {
|
|
131
115
|
return this.computeInverse() !== null;
|
132
116
|
}
|
133
117
|
|
134
|
-
private cachedInverse: Mat33|undefined|null = undefined;
|
135
|
-
private computeInverse(): Mat33|null {
|
118
|
+
private cachedInverse: Mat33 | undefined | null = undefined;
|
119
|
+
private computeInverse(): Mat33 | null {
|
136
120
|
if (this.cachedInverse !== undefined) {
|
137
121
|
return this.cachedInverse;
|
138
122
|
}
|
139
123
|
|
140
|
-
const toIdentity = [
|
141
|
-
this.rows[0],
|
142
|
-
this.rows[1],
|
143
|
-
this.rows[2],
|
144
|
-
];
|
124
|
+
const toIdentity = [this.rows[0], this.rows[1], this.rows[2]];
|
145
125
|
|
146
|
-
const toResult = [
|
147
|
-
Vec3.unitX,
|
148
|
-
Vec3.unitY,
|
149
|
-
Vec3.unitZ,
|
150
|
-
];
|
126
|
+
const toResult = [Vec3.unitX, Vec3.unitY, Vec3.unitZ];
|
151
127
|
|
152
128
|
// Convert toIdentity to the identity matrix and
|
153
129
|
// toResult to the inverse through elementary row operations
|
@@ -199,29 +175,27 @@ export class Mat33 {
|
|
199
175
|
for (let i = 1; i <= 2; i++) {
|
200
176
|
const otherRowIdx = (cursor + i) % 3;
|
201
177
|
scale = -toIdentity[otherRowIdx].at(cursor);
|
202
|
-
toIdentity[otherRowIdx] = toIdentity[otherRowIdx].plus(
|
203
|
-
|
204
|
-
);
|
205
|
-
toResult[otherRowIdx] = toResult[otherRowIdx].plus(
|
206
|
-
cursorToResultRow.times(scale)
|
207
|
-
);
|
178
|
+
toIdentity[otherRowIdx] = toIdentity[otherRowIdx].plus(cursorToIdentityRow.times(scale));
|
179
|
+
toResult[otherRowIdx] = toResult[otherRowIdx].plus(cursorToResultRow.times(scale));
|
208
180
|
}
|
209
181
|
}
|
210
182
|
|
211
|
-
const inverse = Mat33.ofRows(
|
212
|
-
toResult[0],
|
213
|
-
toResult[1],
|
214
|
-
toResult[2]
|
215
|
-
);
|
183
|
+
const inverse = Mat33.ofRows(toResult[0], toResult[1], toResult[2]);
|
216
184
|
this.cachedInverse = inverse;
|
217
185
|
return inverse;
|
218
186
|
}
|
219
187
|
|
220
188
|
public transposed(): Mat33 {
|
221
189
|
return new Mat33(
|
222
|
-
this.a1,
|
223
|
-
this.
|
224
|
-
this.
|
190
|
+
this.a1,
|
191
|
+
this.b1,
|
192
|
+
this.c1,
|
193
|
+
this.a2,
|
194
|
+
this.b2,
|
195
|
+
this.c2,
|
196
|
+
this.a3,
|
197
|
+
this.b3,
|
198
|
+
this.c3,
|
225
199
|
);
|
226
200
|
}
|
227
201
|
|
@@ -256,9 +230,15 @@ export class Mat33 {
|
|
256
230
|
};
|
257
231
|
|
258
232
|
return new Mat33(
|
259
|
-
at(0, 0),
|
260
|
-
at(
|
261
|
-
at(
|
233
|
+
at(0, 0),
|
234
|
+
at(0, 1),
|
235
|
+
at(0, 2),
|
236
|
+
at(1, 0),
|
237
|
+
at(1, 1),
|
238
|
+
at(1, 2),
|
239
|
+
at(2, 0),
|
240
|
+
at(2, 1),
|
241
|
+
at(2, 2),
|
262
242
|
);
|
263
243
|
}
|
264
244
|
|
@@ -288,11 +268,7 @@ export class Mat33 {
|
|
288
268
|
* This is the standard way of transforming vectors in ℝ³.
|
289
269
|
*/
|
290
270
|
public transformVec3(other: Vec3): Vec3 {
|
291
|
-
return Vec3.of(
|
292
|
-
this.rows[0].dot(other),
|
293
|
-
this.rows[1].dot(other),
|
294
|
-
this.rows[2].dot(other)
|
295
|
-
);
|
271
|
+
return Vec3.of(this.rows[0].dot(other), this.rows[1].dot(other), this.rows[2].dot(other));
|
296
272
|
}
|
297
273
|
|
298
274
|
/** @returns true iff this is the identity matrix. */
|
@@ -326,7 +302,7 @@ export class Mat33 {
|
|
326
302
|
*/
|
327
303
|
public toString(): string {
|
328
304
|
let result = '';
|
329
|
-
const maxColumnLens = [
|
305
|
+
const maxColumnLens = [0, 0, 0];
|
330
306
|
|
331
307
|
// Determine the longest item in each column so we can pad the others to that
|
332
308
|
// length.
|
@@ -390,11 +366,7 @@ export class Mat33 {
|
|
390
366
|
* ```
|
391
367
|
*/
|
392
368
|
public toArray(): Mat33Array {
|
393
|
-
return [
|
394
|
-
this.a1, this.a2, this.a3,
|
395
|
-
this.b1, this.b2, this.b3,
|
396
|
-
this.c1, this.c2, this.c3,
|
397
|
-
];
|
369
|
+
return [this.a1, this.a2, this.a3, this.b1, this.b2, this.b3, this.c1, this.c2, this.c3];
|
398
370
|
}
|
399
371
|
|
400
372
|
/**
|
@@ -413,11 +385,17 @@ export class Mat33 {
|
|
413
385
|
* // ⎣ 6, 7, 8 ⎦
|
414
386
|
* ```
|
415
387
|
*/
|
416
|
-
public mapEntries(mapping: (component: number, rowcol: [number, number])=>number): Mat33 {
|
388
|
+
public mapEntries(mapping: (component: number, rowcol: [number, number]) => number): Mat33 {
|
417
389
|
return new Mat33(
|
418
|
-
mapping(this.a1, [0, 0]),
|
419
|
-
mapping(this.
|
420
|
-
mapping(this.
|
390
|
+
mapping(this.a1, [0, 0]),
|
391
|
+
mapping(this.a2, [0, 1]),
|
392
|
+
mapping(this.a3, [0, 2]),
|
393
|
+
mapping(this.b1, [1, 0]),
|
394
|
+
mapping(this.b2, [1, 1]),
|
395
|
+
mapping(this.b3, [1, 2]),
|
396
|
+
mapping(this.c1, [2, 0]),
|
397
|
+
mapping(this.c2, [2, 1]),
|
398
|
+
mapping(this.c3, [2, 2]),
|
421
399
|
);
|
422
400
|
}
|
423
401
|
|
@@ -428,11 +406,7 @@ export class Mat33 {
|
|
428
406
|
|
429
407
|
/** Returns the `idx`-th column (`idx` is 0-indexed). */
|
430
408
|
public getColumn(idx: number) {
|
431
|
-
return Vec3.of(
|
432
|
-
this.rows[0].at(idx),
|
433
|
-
this.rows[1].at(idx),
|
434
|
-
this.rows[2].at(idx),
|
435
|
-
);
|
409
|
+
return Vec3.of(this.rows[0].at(idx), this.rows[1].at(idx), this.rows[2].at(idx));
|
436
410
|
}
|
437
411
|
|
438
412
|
/** Returns the magnitude of the entry with the largest entry */
|
@@ -463,11 +437,7 @@ export class Mat33 {
|
|
463
437
|
// Vec2s z = 1. As such,
|
464
438
|
// outVec2.x = inVec2.x * 1 + inVec2.y * 0 + 1 * amount.x
|
465
439
|
// ...
|
466
|
-
return new Mat33(
|
467
|
-
1, 0, amount.x,
|
468
|
-
0, 1, amount.y,
|
469
|
-
0, 0, 1
|
470
|
-
);
|
440
|
+
return new Mat33(1, 0, amount.x, 0, 1, amount.y, 0, 0, 1);
|
471
441
|
}
|
472
442
|
|
473
443
|
public static zRotation(radians: number, center: Point2 = Vec2.zero): Mat33 {
|
@@ -481,15 +451,11 @@ export class Mat33 {
|
|
481
451
|
// Translate everything so that rotation is about the origin
|
482
452
|
let result = Mat33.translation(center);
|
483
453
|
|
484
|
-
result = result.rightMul(new Mat33(
|
485
|
-
cos, -sin, 0,
|
486
|
-
sin, cos, 0,
|
487
|
-
0, 0, 1
|
488
|
-
));
|
454
|
+
result = result.rightMul(new Mat33(cos, -sin, 0, sin, cos, 0, 0, 0, 1));
|
489
455
|
return result.rightMul(Mat33.translation(center.times(-1)));
|
490
456
|
}
|
491
457
|
|
492
|
-
public static scaling2D(amount: number|Vec2, center: Point2 = Vec2.zero): Mat33 {
|
458
|
+
public static scaling2D(amount: number | Vec2, center: Point2 = Vec2.zero): Mat33 {
|
493
459
|
let result = Mat33.translation(center);
|
494
460
|
let xAmount, yAmount;
|
495
461
|
|
@@ -501,11 +467,7 @@ export class Mat33 {
|
|
501
467
|
yAmount = amount.y;
|
502
468
|
}
|
503
469
|
|
504
|
-
result = result.rightMul(new Mat33(
|
505
|
-
xAmount, 0, 0,
|
506
|
-
0, yAmount, 0,
|
507
|
-
0, 0, 1
|
508
|
-
));
|
470
|
+
result = result.rightMul(new Mat33(xAmount, 0, 0, 0, yAmount, 0, 0, 0, 1));
|
509
471
|
|
510
472
|
// Translate such that [center] goes to (0, 0)
|
511
473
|
return result.rightMul(Mat33.translation(center.times(-1)));
|
@@ -536,7 +498,7 @@ export class Mat33 {
|
|
536
498
|
}
|
537
499
|
|
538
500
|
const parseArguments = (argumentString: string): number[] => {
|
539
|
-
const parsed = argumentString.split(/[, \t\n]+/g).map(argString => {
|
501
|
+
const parsed = argumentString.split(/[, \t\n]+/g).map((argString) => {
|
540
502
|
// Handle trailing spaces/commands
|
541
503
|
if (argString.trim() === '') {
|
542
504
|
return null;
|
@@ -549,18 +511,16 @@ export class Mat33 {
|
|
549
511
|
}
|
550
512
|
|
551
513
|
// Remove trailing px units.
|
552
|
-
argString = argString.replace(/px$/
|
514
|
+
argString = argString.replace(/px$/gi, '');
|
553
515
|
|
554
516
|
const numberExp = /^[-]?\d*(?:\.\d*)?(?:[eE][-+]?\d+)?$/i;
|
555
517
|
|
556
518
|
if (!numberExp.exec(argString)) {
|
557
519
|
throw new Error(
|
558
|
-
`All arguments to transform functions must be numeric (state: ${
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
})
|
563
|
-
})`
|
520
|
+
`All arguments to transform functions must be numeric (state: ${JSON.stringify({
|
521
|
+
currentArgument: argString,
|
522
|
+
allArguments: argumentString,
|
523
|
+
})})`,
|
564
524
|
);
|
565
525
|
}
|
566
526
|
|
@@ -572,10 +532,9 @@ export class Mat33 {
|
|
572
532
|
|
573
533
|
return argNumber;
|
574
534
|
});
|
575
|
-
return parsed.filter(n => n !== null);
|
535
|
+
return parsed.filter((n) => n !== null);
|
576
536
|
};
|
577
537
|
|
578
|
-
|
579
538
|
const keywordToAction = {
|
580
539
|
matrix: (matrixData: number[]) => {
|
581
540
|
if (matrixData.length !== 6) {
|
@@ -589,11 +548,7 @@ export class Mat33 {
|
|
589
548
|
const e = matrixData[4];
|
590
549
|
const f = matrixData[5];
|
591
550
|
|
592
|
-
const transform = new Mat33(
|
593
|
-
a, c, e,
|
594
|
-
b, d, f,
|
595
|
-
0, 0, 1
|
596
|
-
);
|
551
|
+
const transform = new Mat33(a, c, e, b, d, f, 0, 0, 1);
|
597
552
|
return transform;
|
598
553
|
},
|
599
554
|
|
@@ -623,7 +578,9 @@ export class Mat33 {
|
|
623
578
|
translateX = translateArgs[0];
|
624
579
|
translateY = translateArgs[1];
|
625
580
|
} else {
|
626
|
-
throw new Error(
|
581
|
+
throw new Error(
|
582
|
+
`The translate() function requires either 1 or 2 arguments. Given ${translateArgs}`,
|
583
|
+
);
|
627
584
|
}
|
628
585
|
|
629
586
|
return Mat33.translation(Vec2.of(translateX, translateY));
|
@@ -632,9 +589,9 @@ export class Mat33 {
|
|
632
589
|
|
633
590
|
// A command (\w+)
|
634
591
|
// followed by a set of arguments ([ \t\n0-9eE.,\-%]+)
|
635
|
-
const partRegex = /\s*(\w+)\s*\(([^)]*)\)/
|
592
|
+
const partRegex = /\s*(\w+)\s*\(([^)]*)\)/gi;
|
636
593
|
let match;
|
637
|
-
let matrix: Mat33|null = null;
|
594
|
+
let matrix: Mat33 | null = null;
|
638
595
|
|
639
596
|
while ((match = partRegex.exec(cssString)) !== null) {
|
640
597
|
const action = match[1].toLowerCase();
|
package/src/Vec2.test.ts
CHANGED
@@ -17,11 +17,11 @@ describe('Vec2', () => {
|
|
17
17
|
});
|
18
18
|
|
19
19
|
it('More complicated expressions', () => {
|
20
|
-
expect(
|
20
|
+
expect(Vec2.of(1, 2).plus(Vec2.of(3, 4)).times(2)).objEq(Vec2.of(8, 12));
|
21
21
|
});
|
22
22
|
|
23
23
|
it('Angle', () => {
|
24
|
-
expect(Vec2.of(-1, 1).angle()).toBeCloseTo(3 * Math.PI / 4);
|
24
|
+
expect(Vec2.of(-1, 1).angle()).toBeCloseTo((3 * Math.PI) / 4);
|
25
25
|
});
|
26
26
|
|
27
27
|
it('Perpindicular', () => {
|
@@ -29,4 +29,4 @@ describe('Vec2', () => {
|
|
29
29
|
expect(Vec2.unitX.cross(Vec3.unitZ)).objEq(Vec2.unitY.times(-1), tolerance);
|
30
30
|
expect(Vec2.unitX.orthog()).objEq(Vec2.unitY, tolerance);
|
31
31
|
});
|
32
|
-
});
|
32
|
+
});
|
package/src/Vec3.test.ts
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
import Vec3 from './Vec3';
|
3
2
|
|
4
3
|
describe('Vec3', () => {
|
@@ -60,7 +59,7 @@ describe('Vec3', () => {
|
|
60
59
|
{ from: Vec3.of(-1, -10, 0), to: Vec3.of(1, 2, 0), expected: 148 },
|
61
60
|
])(
|
62
61
|
'.squareDistanceTo and .distanceTo should return correct square and euclidean distances (%j)',
|
63
|
-
({ from
|
62
|
+
({ from, to, expected }) => {
|
64
63
|
expect(from.squareDistanceTo(to)).toBe(expected);
|
65
64
|
expect(to.squareDistanceTo(from)).toBe(expected);
|
66
65
|
expect(to.distanceTo(from)).toBeCloseTo(Math.sqrt(expected));
|
@@ -83,4 +82,4 @@ describe('Vec3', () => {
|
|
83
82
|
expect(a.eq(b, tolerance)).toBe(eq);
|
84
83
|
expect(b.eq(a, tolerance)).toBe(eq);
|
85
84
|
});
|
86
|
-
});
|
85
|
+
});
|
package/src/Vec3.ts
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
|
2
|
-
|
3
1
|
/**
|
4
2
|
* A vector with three components, $\begin{pmatrix} x \\ y \\ z \end{pmatrix}$.
|
5
3
|
* Can also be used to represent a two-component vector.
|
@@ -28,7 +26,7 @@ export interface Vec3 {
|
|
28
26
|
* Returns the x, y components of this.
|
29
27
|
* May be implemented as a getter method.
|
30
28
|
*/
|
31
|
-
readonly xy: { x: number
|
29
|
+
readonly xy: { x: number; y: number };
|
32
30
|
|
33
31
|
/** Returns the vector's `idx`th component. For example, `Vec3.of(1, 2, 3).at(1) → 2`. */
|
34
32
|
at(i: number): number;
|
@@ -124,7 +122,7 @@ export interface Vec3 {
|
|
124
122
|
* Vec3.of(1, 2, 3).scale(Vec3.of(2, 4, 6)); // → Vec3(2, 8, 18)
|
125
123
|
* ```
|
126
124
|
*/
|
127
|
-
scale(other: Vec3|number): Vec3;
|
125
|
+
scale(other: Vec3 | number): Vec3;
|
128
126
|
|
129
127
|
/**
|
130
128
|
* Returns a vector orthogonal to this. If this is a Vec2, returns `this` rotated
|
@@ -154,9 +152,7 @@ export interface Vec3 {
|
|
154
152
|
* console.log(zipped.toString()); // → Vec(0.5, 2, 2.9)
|
155
153
|
* ```
|
156
154
|
*/
|
157
|
-
zip(
|
158
|
-
other: Vec3, zip: (componentInThis: number, componentInOther: number)=> number
|
159
|
-
): Vec3;
|
155
|
+
zip(other: Vec3, zip: (componentInThis: number, componentInOther: number) => number): Vec3;
|
160
156
|
|
161
157
|
/**
|
162
158
|
* Returns a vector with each component acted on by `fn`.
|
@@ -167,10 +163,9 @@ export interface Vec3 {
|
|
167
163
|
* console.log(Vec3.of(1, 2, 3).map(val => val + 1)); // → Vec(2, 3, 4)
|
168
164
|
* ```
|
169
165
|
*/
|
170
|
-
map(fn: (component: number, index: number)=> number): Vec3;
|
171
|
-
|
172
|
-
asArray(): [ number, number, number ];
|
166
|
+
map(fn: (component: number, index: number) => number): Vec3;
|
173
167
|
|
168
|
+
asArray(): [number, number, number];
|
174
169
|
|
175
170
|
/**
|
176
171
|
* [fuzz] The maximum difference between two components for this and [other]
|
@@ -196,9 +191,8 @@ class Vec3Impl implements Vec3 {
|
|
196
191
|
public constructor(
|
197
192
|
public readonly x: number,
|
198
193
|
public readonly y: number,
|
199
|
-
public readonly z: number
|
200
|
-
) {
|
201
|
-
}
|
194
|
+
public readonly z: number,
|
195
|
+
) {}
|
202
196
|
|
203
197
|
public get xy(): { x: number; y: number } {
|
204
198
|
// Useful for APIs that behave differently if .z is present.
|
@@ -288,16 +282,12 @@ class Vec3Impl implements Vec3 {
|
|
288
282
|
);
|
289
283
|
}
|
290
284
|
|
291
|
-
public scale(other: Vec3|number): Vec3 {
|
285
|
+
public scale(other: Vec3 | number): Vec3 {
|
292
286
|
if (typeof other === 'number') {
|
293
287
|
return this.times(other);
|
294
288
|
}
|
295
289
|
|
296
|
-
return Vec3.of(
|
297
|
-
this.x * other.x,
|
298
|
-
this.y * other.y,
|
299
|
-
this.z * other.z,
|
300
|
-
);
|
290
|
+
return Vec3.of(this.x * other.x, this.y * other.y, this.z * other.z);
|
301
291
|
}
|
302
292
|
|
303
293
|
public orthog(): Vec3 {
|
@@ -318,28 +308,25 @@ class Vec3Impl implements Vec3 {
|
|
318
308
|
}
|
319
309
|
|
320
310
|
public zip(
|
321
|
-
other: Vec3,
|
311
|
+
other: Vec3,
|
312
|
+
zip: (componentInThis: number, componentInOther: number) => number,
|
322
313
|
): Vec3 {
|
323
|
-
return Vec3.of(
|
324
|
-
zip(other.x, this.x),
|
325
|
-
zip(other.y, this.y),
|
326
|
-
zip(other.z, this.z)
|
327
|
-
);
|
314
|
+
return Vec3.of(zip(other.x, this.x), zip(other.y, this.y), zip(other.z, this.z));
|
328
315
|
}
|
329
316
|
|
330
|
-
public map(fn: (component: number, index: number)=> number): Vec3 {
|
317
|
+
public map(fn: (component: number, index: number) => number): Vec3 {
|
331
318
|
return Vec3.of(fn(this.x, 0), fn(this.y, 1), fn(this.z, 2));
|
332
319
|
}
|
333
320
|
|
334
|
-
public asArray(): [
|
321
|
+
public asArray(): [number, number, number] {
|
335
322
|
return [this.x, this.y, this.z];
|
336
323
|
}
|
337
324
|
|
338
325
|
public eq(other: Vec3, fuzz: number = defaultEqlTolerance): boolean {
|
339
326
|
return (
|
340
|
-
Math.abs(other.x - this.x) <= fuzz
|
341
|
-
|
342
|
-
|
327
|
+
Math.abs(other.x - this.x) <= fuzz &&
|
328
|
+
Math.abs(other.y - this.y) <= fuzz &&
|
329
|
+
Math.abs(other.z - this.z) <= fuzz
|
343
330
|
);
|
344
331
|
}
|
345
332
|
|
@@ -352,10 +339,11 @@ class Vec2Impl implements Vec3 {
|
|
352
339
|
public constructor(
|
353
340
|
public readonly x: number,
|
354
341
|
public readonly y: number,
|
355
|
-
) {
|
356
|
-
}
|
342
|
+
) {}
|
357
343
|
|
358
|
-
public get z() {
|
344
|
+
public get z() {
|
345
|
+
return 0;
|
346
|
+
}
|
359
347
|
|
360
348
|
public get xy(): { x: number; y: number } {
|
361
349
|
// Useful for APIs that behave differently if .z is present.
|
@@ -436,22 +424,15 @@ class Vec2Impl implements Vec3 {
|
|
436
424
|
// | i j k |
|
437
425
|
// | x1 y1 z1| = (i)(y1z2 - y2z1) - (j)(x1z2 - x2z1) + (k)(x1y2 - x2y1)
|
438
426
|
// | x2 y2 z2|
|
439
|
-
return Vec3.of(
|
440
|
-
this.y * other.z,
|
441
|
-
-this.x * other.z,
|
442
|
-
this.x * other.y - other.x * this.y,
|
443
|
-
);
|
427
|
+
return Vec3.of(this.y * other.z, -this.x * other.z, this.x * other.y - other.x * this.y);
|
444
428
|
}
|
445
429
|
|
446
|
-
public scale(other: Vec3|number): Vec3 {
|
430
|
+
public scale(other: Vec3 | number): Vec3 {
|
447
431
|
if (typeof other === 'number') {
|
448
432
|
return this.times(other);
|
449
433
|
}
|
450
434
|
|
451
|
-
return Vec2.of(
|
452
|
-
this.x * other.x,
|
453
|
-
this.y * other.y,
|
454
|
-
);
|
435
|
+
return Vec2.of(this.x * other.x, this.y * other.y);
|
455
436
|
}
|
456
437
|
|
457
438
|
public orthog(): Vec3 {
|
@@ -472,30 +453,25 @@ class Vec2Impl implements Vec3 {
|
|
472
453
|
}
|
473
454
|
|
474
455
|
public zip(
|
475
|
-
other: Vec3,
|
456
|
+
other: Vec3,
|
457
|
+
zip: (componentInThis: number, componentInOther: number) => number,
|
476
458
|
): Vec3 {
|
477
|
-
return Vec3.of(
|
478
|
-
zip(other.x, this.x),
|
479
|
-
zip(other.y, this.y),
|
480
|
-
zip(other.z, 0),
|
481
|
-
);
|
459
|
+
return Vec3.of(zip(other.x, this.x), zip(other.y, this.y), zip(other.z, 0));
|
482
460
|
}
|
483
461
|
|
484
|
-
public map(fn: (component: number, index: number)=> number): Vec3 {
|
485
|
-
return Vec3.of(
|
486
|
-
fn(this.x, 0), fn(this.y, 1), fn(0, 2)
|
487
|
-
);
|
462
|
+
public map(fn: (component: number, index: number) => number): Vec3 {
|
463
|
+
return Vec3.of(fn(this.x, 0), fn(this.y, 1), fn(0, 2));
|
488
464
|
}
|
489
465
|
|
490
|
-
public asArray(): [
|
466
|
+
public asArray(): [number, number, number] {
|
491
467
|
return [this.x, this.y, 0];
|
492
468
|
}
|
493
469
|
|
494
470
|
public eq(other: Vec3, fuzz: number = defaultEqlTolerance): boolean {
|
495
471
|
return (
|
496
|
-
Math.abs(other.x - this.x) <= fuzz
|
497
|
-
|
498
|
-
|
472
|
+
Math.abs(other.x - this.x) <= fuzz &&
|
473
|
+
Math.abs(other.y - this.y) <= fuzz &&
|
474
|
+
Math.abs(other.z) <= fuzz
|
499
475
|
);
|
500
476
|
}
|
501
477
|
|
@@ -537,7 +513,7 @@ export namespace Vec2 {
|
|
537
513
|
* const v2 = Vec2.ofXY({ x: -123.4, y: 1 });
|
538
514
|
* ```
|
539
515
|
*/
|
540
|
-
export const ofXY = ({x, y}: {x: number
|
516
|
+
export const ofXY = ({ x, y }: { x: number; y: number }) => {
|
541
517
|
return Vec2.of(x, y);
|
542
518
|
};
|
543
519
|
|
package/src/lib.ts
CHANGED
@@ -20,7 +20,6 @@
|
|
20
20
|
export { LineSegment2 } from './shapes/LineSegment2';
|
21
21
|
export {
|
22
22
|
Path,
|
23
|
-
|
24
23
|
IntersectionResult as PathIntersectionResult,
|
25
24
|
CurveIndexRecord as PathCurveIndex,
|
26
25
|
stepCurveIndexBy as stepPathIndexBy,
|
@@ -43,6 +42,5 @@ export { Vec3 } from './Vec3';
|
|
43
42
|
export { Color4 } from './Color4';
|
44
43
|
export * from './rounding/lib';
|
45
44
|
|
46
|
-
|
47
45
|
// Note: All above exports cannot use `export { default as ... } from "..."` because this
|
48
46
|
// breaks TypeDoc -- TypeDoc otherwise labels any imports of these classes as `default`.
|
@@ -1,9 +1,8 @@
|
|
1
|
-
|
2
1
|
import solveQuadratic from './solveQuadratic';
|
3
2
|
|
4
3
|
describe('solveQuadratic', () => {
|
5
4
|
it('should solve linear equations', () => {
|
6
|
-
expect(solveQuadratic(0, 1, 2)).toMatchObject([
|
5
|
+
expect(solveQuadratic(0, 1, 2)).toMatchObject([-2, -2]);
|
7
6
|
expect(solveQuadratic(0, 0, 2)[0]).toBeNaN();
|
8
7
|
});
|
9
8
|
|
@@ -11,21 +10,48 @@ describe('solveQuadratic', () => {
|
|
11
10
|
type TestCase = [[number, number, number], [number, number]];
|
12
11
|
|
13
12
|
const testCases: TestCase[] = [
|
14
|
-
[
|
15
|
-
|
13
|
+
[
|
14
|
+
[1, 0, 0],
|
15
|
+
[0, 0],
|
16
|
+
],
|
17
|
+
[
|
18
|
+
[2, 0, 0],
|
19
|
+
[0, 0],
|
20
|
+
],
|
16
21
|
|
17
|
-
[
|
18
|
-
|
19
|
-
|
22
|
+
[
|
23
|
+
[1, 0, -1],
|
24
|
+
[1, -1],
|
25
|
+
],
|
26
|
+
[
|
27
|
+
[1, 0, -4],
|
28
|
+
[2, -2],
|
29
|
+
],
|
30
|
+
[
|
31
|
+
[1, 0, 4],
|
32
|
+
[NaN, NaN],
|
33
|
+
],
|
20
34
|
|
21
|
-
[
|
22
|
-
|
35
|
+
[
|
36
|
+
[1, 1, 0],
|
37
|
+
[0, -1],
|
38
|
+
],
|
39
|
+
[
|
40
|
+
[1, 2, 0],
|
41
|
+
[0, -2],
|
42
|
+
],
|
23
43
|
|
24
|
-
[
|
25
|
-
|
44
|
+
[
|
45
|
+
[1, 2, 1],
|
46
|
+
[-1, -1],
|
47
|
+
],
|
48
|
+
[
|
49
|
+
[-9, 2, 1 / 3],
|
50
|
+
[1 / 3, -1 / 9],
|
51
|
+
],
|
26
52
|
];
|
27
53
|
|
28
|
-
for (const [
|
54
|
+
for (const [testCase, solution] of testCases) {
|
29
55
|
const foundSolutions = solveQuadratic(...testCase);
|
30
56
|
for (let i = 0; i < 2; i++) {
|
31
57
|
if (isNaN(solution[i]) && isNaN(foundSolutions[i])) {
|
@@ -36,4 +62,4 @@ describe('solveQuadratic', () => {
|
|
36
62
|
}
|
37
63
|
}
|
38
64
|
});
|
39
|
-
});
|
65
|
+
});
|
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
/**
|
3
2
|
* Solves an equation of the form ax² + bx + c = 0.
|
4
3
|
* The larger solution is returned first.
|
@@ -21,13 +20,13 @@ const solveQuadratic = (a: number, b: number, c: number): [number, number] => {
|
|
21
20
|
solution = -c / b;
|
22
21
|
}
|
23
22
|
|
24
|
-
return [
|
23
|
+
return [solution, solution];
|
25
24
|
}
|
26
25
|
|
27
26
|
const discriminant = b * b - 4 * a * c;
|
28
27
|
|
29
28
|
if (discriminant < 0) {
|
30
|
-
return [
|
29
|
+
return [NaN, NaN];
|
31
30
|
}
|
32
31
|
|
33
32
|
const rootDiscriminant = Math.sqrt(discriminant);
|
@@ -35,9 +34,9 @@ const solveQuadratic = (a: number, b: number, c: number): [number, number] => {
|
|
35
34
|
const solution2 = (-b - rootDiscriminant) / (2 * a);
|
36
35
|
|
37
36
|
if (solution1 > solution2) {
|
38
|
-
return [
|
37
|
+
return [solution1, solution2];
|
39
38
|
} else {
|
40
|
-
return [
|
39
|
+
return [solution2, solution1];
|
41
40
|
}
|
42
41
|
};
|
43
|
-
export default solveQuadratic;
|
42
|
+
export default solveQuadratic;
|