@js-draw/math 1.21.3 → 1.23.1
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.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/Color4.ts
CHANGED
@@ -25,9 +25,8 @@ export class Color4 {
|
|
25
25
|
public readonly b: number,
|
26
26
|
|
27
27
|
/** Alpha/transparent component. ${\tt a} \in [0, 1]$. 0 = transparent */
|
28
|
-
public readonly a: number
|
29
|
-
) {
|
30
|
-
}
|
28
|
+
public readonly a: number,
|
29
|
+
) {}
|
31
30
|
|
32
31
|
/**
|
33
32
|
* Create a color from red, green, blue components. The color is fully opaque (`a = 1.0`).
|
@@ -38,6 +37,10 @@ export class Color4 {
|
|
38
37
|
return Color4.ofRGBA(red, green, blue, 1.0);
|
39
38
|
}
|
40
39
|
|
40
|
+
/**
|
41
|
+
* Creates a color from red, green, blue, and transparency components. Each component should
|
42
|
+
* be in the range $[0, 1]$.
|
43
|
+
*/
|
41
44
|
public static ofRGBA(red: number, green: number, blue: number, alpha: number): Color4 {
|
42
45
|
red = Math.max(0, Math.min(red, 1));
|
43
46
|
green = Math.max(0, Math.min(green, 1));
|
@@ -47,6 +50,40 @@ export class Color4 {
|
|
47
50
|
return new Color4(red, green, blue, alpha);
|
48
51
|
}
|
49
52
|
|
53
|
+
/**
|
54
|
+
* Creates a color from an RGB (or RGBA) array.
|
55
|
+
*
|
56
|
+
* This is similar to {@link ofRGB} and {@link ofRGBA}, but, by default, takes values
|
57
|
+
* that range from 0 to 255.
|
58
|
+
*
|
59
|
+
* If the array values instead range from 0-1, pass `maxValue` as `1`.
|
60
|
+
*/
|
61
|
+
public static fromRGBArray(
|
62
|
+
array: Uint8Array | Uint8ClampedArray | number[],
|
63
|
+
maxValue: number = 255,
|
64
|
+
) {
|
65
|
+
const red = array[0];
|
66
|
+
const green = array[1] ?? red;
|
67
|
+
const blue = array[2] ?? red;
|
68
|
+
|
69
|
+
let alpha = 255;
|
70
|
+
if (3 < array.length) {
|
71
|
+
alpha = array[3];
|
72
|
+
}
|
73
|
+
|
74
|
+
return Color4.ofRGBA(red / maxValue, green / maxValue, blue / maxValue, alpha / maxValue);
|
75
|
+
}
|
76
|
+
|
77
|
+
/**
|
78
|
+
* Creates a `Color4` from a three or four-component hexadecimal
|
79
|
+
* [color string](https://en.wikipedia.org/wiki/Web_colors#Hex_triplet).
|
80
|
+
*
|
81
|
+
* Example:
|
82
|
+
* ```ts,runnable,console
|
83
|
+
* import { Color4 } from '@js-draw/math';
|
84
|
+
* console.log(Color4.fromHex('#ff0'));
|
85
|
+
* ```
|
86
|
+
*/
|
50
87
|
public static fromHex(hexString: string): Color4 {
|
51
88
|
// Remove starting '#' (if present)
|
52
89
|
hexString = (hexString.match(/^[#]?(.*)$/) ?? [])[1];
|
@@ -62,7 +99,7 @@ export class Color4 {
|
|
62
99
|
const components = hexString.split('');
|
63
100
|
|
64
101
|
// Convert to RRGGBBAA or RRGGBB format
|
65
|
-
hexString = components.map(component => `${component}0`).join('');
|
102
|
+
hexString = components.map((component) => `${component}0`).join('');
|
66
103
|
}
|
67
104
|
|
68
105
|
if (hexString.length === 6) {
|
@@ -83,7 +120,7 @@ export class Color4 {
|
|
83
120
|
return Color4.ofRGBA(components[0], components[1], components[2], components[3]);
|
84
121
|
}
|
85
122
|
|
86
|
-
/** Like fromHex, but can handle additional colors if an `HTMLCanvasElement` is available. */
|
123
|
+
/** Like {@link fromHex}, but can handle additional colors if an `HTMLCanvasElement` is available. */
|
87
124
|
public static fromString(text: string): Color4 {
|
88
125
|
if (text.startsWith('#')) {
|
89
126
|
return Color4.fromHex(text);
|
@@ -108,14 +145,21 @@ export class Color4 {
|
|
108
145
|
|
109
146
|
if (componentsList.length === 3) {
|
110
147
|
return Color4.ofRGB(
|
111
|
-
componentsList[0] / 255,
|
148
|
+
componentsList[0] / 255,
|
149
|
+
componentsList[1] / 255,
|
150
|
+
componentsList[2] / 255,
|
112
151
|
);
|
113
152
|
} else if (componentsList.length === 4) {
|
114
153
|
return Color4.ofRGBA(
|
115
|
-
componentsList[0] / 255,
|
154
|
+
componentsList[0] / 255,
|
155
|
+
componentsList[1] / 255,
|
156
|
+
componentsList[2] / 255,
|
157
|
+
componentsList[3],
|
116
158
|
);
|
117
159
|
} else {
|
118
|
-
throw new Error(
|
160
|
+
throw new Error(
|
161
|
+
`RGB string, ${text}, has wrong number of components: ${componentsList.length}`,
|
162
|
+
);
|
119
163
|
}
|
120
164
|
}
|
121
165
|
|
@@ -145,7 +189,7 @@ export class Color4 {
|
|
145
189
|
}
|
146
190
|
|
147
191
|
/** @returns true if `this` and `other` are approximately equal. */
|
148
|
-
public eq(other: Color4|null|undefined): boolean {
|
192
|
+
public eq(other: Color4 | null | undefined): boolean {
|
149
193
|
if (other == null) {
|
150
194
|
return false;
|
151
195
|
}
|
@@ -202,7 +246,7 @@ export class Color4 {
|
|
202
246
|
// - https://stackoverflow.com/a/9733420
|
203
247
|
|
204
248
|
// Normalize the components, as per above
|
205
|
-
const components = [
|
249
|
+
const components = [this.r, this.g, this.b].map((component) => {
|
206
250
|
if (component < 0.03928) {
|
207
251
|
return component / 12.92;
|
208
252
|
} else {
|
@@ -358,24 +402,23 @@ export class Color4 {
|
|
358
402
|
|
359
403
|
let rgb;
|
360
404
|
if (huePrime < 1) {
|
361
|
-
rgb = [
|
405
|
+
rgb = [chroma, secondLargestComponent, 0];
|
362
406
|
} else if (huePrime < 2) {
|
363
|
-
rgb = [
|
407
|
+
rgb = [secondLargestComponent, chroma, 0];
|
364
408
|
} else if (huePrime < 3) {
|
365
|
-
rgb = [
|
409
|
+
rgb = [0, chroma, secondLargestComponent];
|
366
410
|
} else if (huePrime < 4) {
|
367
|
-
rgb = [
|
411
|
+
rgb = [0, secondLargestComponent, chroma];
|
368
412
|
} else if (huePrime < 5) {
|
369
|
-
rgb = [
|
413
|
+
rgb = [secondLargestComponent, 0, chroma];
|
370
414
|
} else {
|
371
|
-
rgb = [
|
415
|
+
rgb = [chroma, 0, secondLargestComponent];
|
372
416
|
}
|
373
417
|
|
374
418
|
const adjustment = value - chroma;
|
375
419
|
return Color4.ofRGB(rgb[0] + adjustment, rgb[1] + adjustment, rgb[2] + adjustment);
|
376
420
|
}
|
377
421
|
|
378
|
-
|
379
422
|
/**
|
380
423
|
* Equivalent to `ofRGB(rgb.x, rgb.y, rgb.z)`.
|
381
424
|
*
|
@@ -385,7 +428,7 @@ export class Color4 {
|
|
385
428
|
return Color4.ofRGBA(rgb.x, rgb.y, rgb.z, alpha ?? 1);
|
386
429
|
}
|
387
430
|
|
388
|
-
private hexString: string|null = null;
|
431
|
+
private hexString: string | null = null;
|
389
432
|
|
390
433
|
/**
|
391
434
|
* @returns a hexadecimal color string representation of `this`, in the form `#rrggbbaa`.
|
@@ -2,65 +2,51 @@ import Mat33 from './Mat33';
|
|
2
2
|
import { Vec2 } from './Vec2';
|
3
3
|
|
4
4
|
describe('Mat33.fromCSSMatrix', () => {
|
5
|
-
it('should convert CSS matrix(...) strings to
|
5
|
+
it('should convert CSS matrix(...) strings to matrices', () => {
|
6
6
|
// From MDN:
|
7
7
|
// ⎡ a c e ⎤
|
8
8
|
// ⎢ b d f ⎥ = matrix(a,b,c,d,e,f)
|
9
9
|
// ⎣ 0 0 1 ⎦
|
10
10
|
const identity = Mat33.fromCSSMatrix('matrix(1, 0, 0, 1, 0, 0)');
|
11
11
|
expect(identity).objEq(Mat33.identity);
|
12
|
-
expect(Mat33.fromCSSMatrix('matrix(1, 2, 3, 4, 5, 6)')).objEq(
|
13
|
-
1, 3, 5,
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
2, 4, 6,
|
20
|
-
|
21
|
-
))
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
0, 0, 1,
|
26
|
-
)
|
27
|
-
expect(Mat33.fromCSSMatrix('matrix(
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
1.6, .3, 5,
|
34
|
-
2, 4, 6,
|
35
|
-
0, 0, 1,
|
36
|
-
));
|
37
|
-
expect(Mat33.fromCSSMatrix('matrix(1.6,2, .3E-2, 4, 5, 6)')).objEq(new Mat33(
|
38
|
-
1.6, 3e-3, 5,
|
39
|
-
2, 4, 6,
|
40
|
-
0, 0, 1,
|
41
|
-
));
|
42
|
-
expect(Mat33.fromCSSMatrix('matrix(-1, 2e6, 3E-2,-5.123, -6.5e-1, 0.01)')).objEq(new Mat33(
|
43
|
-
-1, 3E-2, -6.5e-1,
|
44
|
-
2e6, -5.123, 0.01,
|
45
|
-
0, 0, 1,
|
46
|
-
));
|
12
|
+
expect(Mat33.fromCSSMatrix('matrix(1, 2, 3, 4, 5, 6)')).objEq(
|
13
|
+
new Mat33(1, 3, 5, 2, 4, 6, 0, 0, 1),
|
14
|
+
);
|
15
|
+
expect(Mat33.fromCSSMatrix('matrix(1e2, 2, 3, 4, 5, 6)')).objEq(
|
16
|
+
new Mat33(1e2, 3, 5, 2, 4, 6, 0, 0, 1),
|
17
|
+
);
|
18
|
+
expect(Mat33.fromCSSMatrix('matrix(1.6, 2, .3, 4, 5, 6)')).objEq(
|
19
|
+
new Mat33(1.6, 0.3, 5, 2, 4, 6, 0, 0, 1),
|
20
|
+
);
|
21
|
+
expect(Mat33.fromCSSMatrix('matrix(-1, 2, 3.E-2, 4, -5.123, -6.5)')).objEq(
|
22
|
+
new Mat33(-1, 0.03, -5.123, 2, 4, -6.5, 0, 0, 1),
|
23
|
+
);
|
24
|
+
expect(Mat33.fromCSSMatrix('matrix(1.6,\n\t2, .3, 4, 5, 6)')).objEq(
|
25
|
+
new Mat33(1.6, 0.3, 5, 2, 4, 6, 0, 0, 1),
|
26
|
+
);
|
27
|
+
expect(Mat33.fromCSSMatrix('matrix(1.6,2, .3E-2, 4, 5, 6)')).objEq(
|
28
|
+
new Mat33(1.6, 3e-3, 5, 2, 4, 6, 0, 0, 1),
|
29
|
+
);
|
30
|
+
expect(Mat33.fromCSSMatrix('matrix(-1, 2e6, 3E-2,-5.123, -6.5e-1, 0.01)')).objEq(
|
31
|
+
new Mat33(-1, 3e-2, -6.5e-1, 2e6, -5.123, 0.01, 0, 0, 1),
|
32
|
+
);
|
47
33
|
});
|
48
34
|
|
49
35
|
it('should convert multi-matrix arguments into a single CSS matrix', () => {
|
50
36
|
const identity = Mat33.fromCSSMatrix('matrix(1, 0, 0, 1, 0, 0) matrix(1, 0, 0, 1, 0, 0)');
|
51
37
|
expect(identity).objEq(Mat33.identity);
|
52
38
|
|
53
|
-
expect(
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
));
|
39
|
+
expect(
|
40
|
+
Mat33.fromCSSMatrix(
|
41
|
+
'matrix(1, 0, 0, 1, 0, 0) matrix(1, 2, 3, 4, 5, 6) matrix(1, 0, 0, 1, 0, 0)',
|
42
|
+
),
|
43
|
+
).objEq(new Mat33(1, 3, 5, 2, 4, 6, 0, 0, 1));
|
58
44
|
|
59
|
-
expect(
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
));
|
45
|
+
expect(
|
46
|
+
Mat33.fromCSSMatrix(
|
47
|
+
'matrix(2,\n\t 0, 0, 2, 0, 0) matrix(1, 2, 3, 4, 5, 6) matrix(1, 0, 0, 1, 0, 0)',
|
48
|
+
),
|
49
|
+
).objEq(new Mat33(2, 6, 10, 4, 8, 12, 0, 0, 1));
|
64
50
|
});
|
65
51
|
|
66
52
|
it('should convert scale()s with a single argument', () => {
|
package/src/Mat33.test.ts
CHANGED
@@ -2,76 +2,44 @@ import Mat33 from './Mat33';
|
|
2
2
|
import { Point2, Vec2 } from './Vec2';
|
3
3
|
import Vec3 from './Vec3';
|
4
4
|
|
5
|
-
|
6
5
|
describe('Mat33 tests', () => {
|
7
6
|
it('equality', () => {
|
8
7
|
expect(Mat33.identity).objEq(Mat33.identity);
|
9
|
-
expect(new Mat33(
|
10
|
-
0.1, 0.
|
11
|
-
0.
|
12
|
-
|
13
|
-
)).objEq(new Mat33(
|
14
|
-
0.2, 0.1, 0.4,
|
15
|
-
0.5, 0.5, 0.7,
|
16
|
-
0.7, 0.8, -0.9
|
17
|
-
), 0.2);
|
8
|
+
expect(new Mat33(0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, -0.9)).objEq(
|
9
|
+
new Mat33(0.2, 0.1, 0.4, 0.5, 0.5, 0.7, 0.7, 0.8, -0.9),
|
10
|
+
0.2,
|
11
|
+
);
|
18
12
|
});
|
19
13
|
|
20
14
|
it('transposition', () => {
|
21
15
|
expect(Mat33.identity.transposed()).objEq(Mat33.identity);
|
22
|
-
expect(new Mat33(
|
23
|
-
1, 2, 0,
|
24
|
-
|
25
|
-
0, 1, 0
|
26
|
-
).transposed()).objEq(new Mat33(
|
27
|
-
1, 0, 0,
|
28
|
-
2, 0, 1,
|
29
|
-
0, 0, 0
|
30
|
-
));
|
16
|
+
expect(new Mat33(1, 2, 0, 0, 0, 0, 0, 1, 0).transposed()).objEq(
|
17
|
+
new Mat33(1, 0, 0, 2, 0, 1, 0, 0, 0),
|
18
|
+
);
|
31
19
|
});
|
32
20
|
|
33
21
|
it('multiplication', () => {
|
34
|
-
const M = new Mat33(
|
35
|
-
1, 2, 3,
|
36
|
-
4, 5, 6,
|
37
|
-
7, 8, 9
|
38
|
-
);
|
22
|
+
const M = new Mat33(1, 2, 3, 4, 5, 6, 7, 8, 9);
|
39
23
|
|
40
24
|
expect(Mat33.identity.rightMul(Mat33.identity)).objEq(Mat33.identity);
|
41
25
|
expect(M.rightMul(Mat33.identity)).objEq(M);
|
42
|
-
expect(M.rightMul(new Mat33(
|
43
|
-
1,
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
4, 10, 6,
|
49
|
-
7, 16, 9
|
50
|
-
));
|
51
|
-
expect(M.rightMul(new Mat33(
|
52
|
-
2, 0, 1,
|
53
|
-
0, 1, 0,
|
54
|
-
0, 0, 3
|
55
|
-
))).objEq(new Mat33(
|
56
|
-
2, 2, 10,
|
57
|
-
8, 5, 22,
|
58
|
-
14, 8, 34
|
59
|
-
));
|
26
|
+
expect(M.rightMul(new Mat33(1, 0, 0, 0, 2, 0, 0, 0, 1))).objEq(
|
27
|
+
new Mat33(1, 4, 3, 4, 10, 6, 7, 16, 9),
|
28
|
+
);
|
29
|
+
expect(M.rightMul(new Mat33(2, 0, 1, 0, 1, 0, 0, 0, 3))).objEq(
|
30
|
+
new Mat33(2, 2, 10, 8, 5, 22, 14, 8, 34),
|
31
|
+
);
|
60
32
|
});
|
61
33
|
|
62
34
|
it('the inverse of the identity matrix should be the identity matrix', () => {
|
63
35
|
const fuzz = 0.01;
|
64
36
|
expect(Mat33.identity.inverse()).objEq(Mat33.identity, fuzz);
|
65
37
|
|
66
|
-
const M = new Mat33(
|
67
|
-
1, 2, 3,
|
68
|
-
4, 1, 0,
|
69
|
-
2, 3, 0
|
70
|
-
);
|
38
|
+
const M = new Mat33(1, 2, 3, 4, 1, 0, 2, 3, 0);
|
71
39
|
expect(M.inverse().rightMul(M)).objEq(Mat33.identity, fuzz);
|
72
40
|
});
|
73
41
|
|
74
|
-
it('90 degree z-rotation
|
42
|
+
it('90 degree z-rotation matrices should rotate 90 degrees counter clockwise', () => {
|
75
43
|
const fuzz = 0.001;
|
76
44
|
|
77
45
|
const M = Mat33.zRotation(Math.PI / 2);
|
@@ -80,19 +48,19 @@ describe('Mat33 tests', () => {
|
|
80
48
|
expect(M.transformVec2(rotated)).objEq(Vec2.unitX.times(-1), fuzz);
|
81
49
|
});
|
82
50
|
|
83
|
-
it('z-rotation
|
51
|
+
it('z-rotation matrices should preserve the given origin', () => {
|
84
52
|
const testPairs: Array<[number, Vec2]> = [
|
85
|
-
[
|
86
|
-
[
|
87
|
-
[
|
53
|
+
[Math.PI / 2, Vec2.zero],
|
54
|
+
[-Math.PI / 2, Vec2.zero],
|
55
|
+
[-Math.PI / 2, Vec2.of(10, 10)],
|
88
56
|
];
|
89
57
|
|
90
|
-
for (const [
|
58
|
+
for (const [angle, center] of testPairs) {
|
91
59
|
expect(Mat33.zRotation(angle, center).transformVec2(center)).objEq(center);
|
92
60
|
}
|
93
61
|
});
|
94
62
|
|
95
|
-
it('translation
|
63
|
+
it('translation matrices should translate Vec2s', () => {
|
96
64
|
const fuzz = 0.01;
|
97
65
|
|
98
66
|
const M = Mat33.translation(Vec2.of(1, -4));
|
@@ -100,7 +68,7 @@ describe('Mat33 tests', () => {
|
|
100
68
|
expect(M.transformVec2(Vec2.of(-1, 3))).objEq(Vec2.of(0, -1), fuzz);
|
101
69
|
});
|
102
70
|
|
103
|
-
it('scaling
|
71
|
+
it('scaling matrices should scale about the provided center', () => {
|
104
72
|
const fuzz = 0.01;
|
105
73
|
|
106
74
|
const center = Vec2.of(1, -4);
|
@@ -109,29 +77,29 @@ describe('Mat33 tests', () => {
|
|
109
77
|
expect(M.transformVec2(Vec2.of(0, 0))).objEq(Vec2.of(-1, 4), fuzz);
|
110
78
|
});
|
111
79
|
|
112
|
-
it('calling inverse on singular
|
80
|
+
it('calling inverse on singular matrices should result in the identity matrix', () => {
|
113
81
|
const fuzz = 0.001;
|
114
|
-
const singularMat = Mat33.ofRows(
|
115
|
-
Vec3.of(0, 0, 1),
|
116
|
-
Vec3.of(0, 1, 0),
|
117
|
-
Vec3.of(0, 1, 1)
|
118
|
-
);
|
82
|
+
const singularMat = Mat33.ofRows(Vec3.of(0, 0, 1), Vec3.of(0, 1, 0), Vec3.of(0, 1, 1));
|
119
83
|
expect(singularMat.invertable()).toBe(false);
|
120
84
|
expect(singularMat.inverse()).objEq(Mat33.identity, fuzz);
|
121
85
|
});
|
122
86
|
|
123
|
-
it('z-rotation
|
87
|
+
it('z-rotation matrices should be invertable', () => {
|
124
88
|
const fuzz = 0.01;
|
125
89
|
const M = Mat33.zRotation(-0.2617993877991494, Vec2.of(481, 329.5));
|
126
|
-
expect(
|
127
|
-
M.inverse().transformVec2(M.transformVec2(Vec2.unitX))
|
128
|
-
).objEq(Vec2.unitX, fuzz);
|
90
|
+
expect(M.inverse().transformVec2(M.transformVec2(Vec2.unitX))).objEq(Vec2.unitX, fuzz);
|
129
91
|
expect(M.invertable());
|
130
92
|
|
131
93
|
const starterTransform = new Mat33(
|
132
|
-
-0.2588190451025205,
|
133
|
-
0.9659258262890688,
|
134
|
-
|
94
|
+
-0.2588190451025205,
|
95
|
+
-0.9659258262890688,
|
96
|
+
923.7645204565603,
|
97
|
+
0.9659258262890688,
|
98
|
+
-0.2588190451025205,
|
99
|
+
-49.829447083761465,
|
100
|
+
0,
|
101
|
+
0,
|
102
|
+
1,
|
135
103
|
);
|
136
104
|
expect(starterTransform.invertable()).toBe(true);
|
137
105
|
|
@@ -139,32 +107,32 @@ describe('Mat33 tests', () => {
|
|
139
107
|
const fullTransformInverse = fullTransform.inverse();
|
140
108
|
expect(fullTransform.invertable()).toBe(true);
|
141
109
|
|
142
|
-
expect(
|
143
|
-
fullTransformInverse.rightMul(fullTransform)
|
144
|
-
).objEq(Mat33.identity, fuzz);
|
110
|
+
expect(fullTransformInverse.rightMul(fullTransform)).objEq(Mat33.identity, fuzz);
|
145
111
|
|
146
|
-
expect(
|
147
|
-
|
148
|
-
|
112
|
+
expect(fullTransform.transformVec2(fullTransformInverse.transformVec2(Vec2.unitX))).objEq(
|
113
|
+
Vec2.unitX,
|
114
|
+
fuzz,
|
115
|
+
);
|
149
116
|
|
150
|
-
expect(
|
151
|
-
|
152
|
-
|
117
|
+
expect(fullTransformInverse.transformVec2(fullTransform.transformVec2(Vec2.unitX))).objEq(
|
118
|
+
Vec2.unitX,
|
119
|
+
fuzz,
|
120
|
+
);
|
153
121
|
});
|
154
122
|
|
155
123
|
it('z-rotation matrix inverses should undo the z-rotation', () => {
|
156
|
-
const testCases: Array<[
|
157
|
-
[
|
158
|
-
[
|
159
|
-
[
|
160
|
-
[
|
161
|
-
[
|
162
|
-
[
|
163
|
-
[
|
124
|
+
const testCases: Array<[number, Point2]> = [
|
125
|
+
[Math.PI / 2, Vec2.zero],
|
126
|
+
[Math.PI, Vec2.of(1, 1)],
|
127
|
+
[-Math.PI, Vec2.of(1, 1)],
|
128
|
+
[-Math.PI * 2, Vec2.of(1, 1)],
|
129
|
+
[-Math.PI * 2, Vec2.of(123, 456)],
|
130
|
+
[-Math.PI / 4, Vec2.of(123, 456)],
|
131
|
+
[0.1, Vec2.of(1, 2)],
|
164
132
|
];
|
165
133
|
|
166
134
|
const fuzz = 0.00001;
|
167
|
-
for (const [
|
135
|
+
for (const [angle, center] of testCases) {
|
168
136
|
const mat = Mat33.zRotation(angle, center);
|
169
137
|
expect(mat.inverse().rightMul(mat)).objEq(Mat33.identity, fuzz);
|
170
138
|
expect(mat.rightMul(mat.inverse())).objEq(Mat33.identity, fuzz);
|
@@ -172,30 +140,24 @@ describe('Mat33 tests', () => {
|
|
172
140
|
});
|
173
141
|
|
174
142
|
it('z-rotation should preserve given origin', () => {
|
175
|
-
const testCases: Array<[
|
176
|
-
[
|
177
|
-
[
|
178
|
-
[
|
179
|
-
[
|
143
|
+
const testCases: Array<[number, Point2]> = [
|
144
|
+
[6.205048847547065, Vec2.of(75.16363373235318, 104.29870408043762)],
|
145
|
+
[1.234, Vec2.of(-56, 789)],
|
146
|
+
[-Math.PI, Vec2.of(-56, 789)],
|
147
|
+
[-Math.PI / 2, Vec2.of(-0.001, 1.0002)],
|
180
148
|
];
|
181
149
|
|
182
150
|
for (const [angle, rotationOrigin] of testCases) {
|
183
|
-
expect(Mat33.zRotation(angle, rotationOrigin).transformVec2(rotationOrigin)).objEq(
|
151
|
+
expect(Mat33.zRotation(angle, rotationOrigin).transformVec2(rotationOrigin)).objEq(
|
152
|
+
rotationOrigin,
|
153
|
+
);
|
184
154
|
}
|
185
155
|
});
|
186
156
|
|
187
157
|
it('should correctly apply a mapping to all components', () => {
|
188
158
|
expect(
|
189
|
-
new Mat33(
|
190
|
-
|
191
|
-
4, 5, 6,
|
192
|
-
7, 8, 9,
|
193
|
-
).mapEntries(component => component - 1)
|
194
|
-
).toMatchObject(new Mat33(
|
195
|
-
0, 1, 2,
|
196
|
-
3, 4, 5,
|
197
|
-
6, 7, 8,
|
198
|
-
));
|
159
|
+
new Mat33(1, 2, 3, 4, 5, 6, 7, 8, 9).mapEntries((component) => component - 1),
|
160
|
+
).toMatchObject(new Mat33(0, 1, 2, 3, 4, 5, 6, 7, 8));
|
199
161
|
});
|
200
162
|
|
201
163
|
it('getColumn should return the given column index', () => {
|