@js-draw/math 1.21.2 → 1.22.0
Sign up to get free protection for your applications and to get access to all the features.
- 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;
|