@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/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', () => {
|