@pawells/math-extended 1.0.1 → 1.0.2

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.
Files changed (85) hide show
  1. package/package.json +1 -1
  2. package/build/angles.spec.d.ts +0 -2
  3. package/build/angles.spec.d.ts.map +0 -1
  4. package/build/angles.spec.js +0 -147
  5. package/build/angles.spec.js.map +0 -1
  6. package/build/clamp.spec.d.ts +0 -2
  7. package/build/clamp.spec.d.ts.map +0 -1
  8. package/build/clamp.spec.js +0 -19
  9. package/build/clamp.spec.js.map +0 -1
  10. package/build/documentation-validation.spec.d.ts +0 -11
  11. package/build/documentation-validation.spec.d.ts.map +0 -1
  12. package/build/documentation-validation.spec.js +0 -401
  13. package/build/documentation-validation.spec.js.map +0 -1
  14. package/build/interpolation.spec.d.ts +0 -2
  15. package/build/interpolation.spec.d.ts.map +0 -1
  16. package/build/interpolation.spec.js +0 -480
  17. package/build/interpolation.spec.js.map +0 -1
  18. package/build/matrices/arithmetic.spec.d.ts +0 -2
  19. package/build/matrices/arithmetic.spec.d.ts.map +0 -1
  20. package/build/matrices/arithmetic.spec.js +0 -915
  21. package/build/matrices/arithmetic.spec.js.map +0 -1
  22. package/build/matrices/asserts.spec.d.ts +0 -2
  23. package/build/matrices/asserts.spec.d.ts.map +0 -1
  24. package/build/matrices/asserts.spec.js +0 -565
  25. package/build/matrices/asserts.spec.js.map +0 -1
  26. package/build/matrices/core.spec.d.ts +0 -2
  27. package/build/matrices/core.spec.d.ts.map +0 -1
  28. package/build/matrices/core.spec.js +0 -634
  29. package/build/matrices/core.spec.js.map +0 -1
  30. package/build/matrices/decompositions.spec.d.ts +0 -2
  31. package/build/matrices/decompositions.spec.d.ts.map +0 -1
  32. package/build/matrices/decompositions.spec.js +0 -195
  33. package/build/matrices/decompositions.spec.js.map +0 -1
  34. package/build/matrices/linear-algebra.spec.d.ts +0 -2
  35. package/build/matrices/linear-algebra.spec.d.ts.map +0 -1
  36. package/build/matrices/linear-algebra.spec.js +0 -355
  37. package/build/matrices/linear-algebra.spec.js.map +0 -1
  38. package/build/matrices/normalization.spec.d.ts +0 -2
  39. package/build/matrices/normalization.spec.d.ts.map +0 -1
  40. package/build/matrices/normalization.spec.js +0 -335
  41. package/build/matrices/normalization.spec.js.map +0 -1
  42. package/build/matrices/transformations.spec.d.ts +0 -2
  43. package/build/matrices/transformations.spec.d.ts.map +0 -1
  44. package/build/matrices/transformations.spec.js +0 -755
  45. package/build/matrices/transformations.spec.js.map +0 -1
  46. package/build/quaternions/asserts.spec.d.ts +0 -2
  47. package/build/quaternions/asserts.spec.d.ts.map +0 -1
  48. package/build/quaternions/asserts.spec.js +0 -320
  49. package/build/quaternions/asserts.spec.js.map +0 -1
  50. package/build/quaternions/conversions.spec.d.ts +0 -2
  51. package/build/quaternions/conversions.spec.d.ts.map +0 -1
  52. package/build/quaternions/conversions.spec.js +0 -344
  53. package/build/quaternions/conversions.spec.js.map +0 -1
  54. package/build/quaternions/core.spec.d.ts +0 -2
  55. package/build/quaternions/core.spec.d.ts.map +0 -1
  56. package/build/quaternions/core.spec.js +0 -294
  57. package/build/quaternions/core.spec.js.map +0 -1
  58. package/build/quaternions/interpolation.spec.d.ts +0 -2
  59. package/build/quaternions/interpolation.spec.d.ts.map +0 -1
  60. package/build/quaternions/interpolation.spec.js +0 -64
  61. package/build/quaternions/interpolation.spec.js.map +0 -1
  62. package/build/quaternions/predefined.spec.d.ts +0 -2
  63. package/build/quaternions/predefined.spec.d.ts.map +0 -1
  64. package/build/quaternions/predefined.spec.js +0 -35
  65. package/build/quaternions/predefined.spec.js.map +0 -1
  66. package/build/random.spec.d.ts +0 -2
  67. package/build/random.spec.d.ts.map +0 -1
  68. package/build/random.spec.js +0 -267
  69. package/build/random.spec.js.map +0 -1
  70. package/build/vectors/asserts.spec.d.ts +0 -2
  71. package/build/vectors/asserts.spec.d.ts.map +0 -1
  72. package/build/vectors/asserts.spec.js +0 -260
  73. package/build/vectors/asserts.spec.js.map +0 -1
  74. package/build/vectors/core.spec.d.ts +0 -2
  75. package/build/vectors/core.spec.d.ts.map +0 -1
  76. package/build/vectors/core.spec.js +0 -343
  77. package/build/vectors/core.spec.js.map +0 -1
  78. package/build/vectors/interpolation.spec.d.ts +0 -2
  79. package/build/vectors/interpolation.spec.d.ts.map +0 -1
  80. package/build/vectors/interpolation.spec.js +0 -378
  81. package/build/vectors/interpolation.spec.js.map +0 -1
  82. package/build/vectors/predefined.spec.d.ts +0 -2
  83. package/build/vectors/predefined.spec.d.ts.map +0 -1
  84. package/build/vectors/predefined.spec.js +0 -333
  85. package/build/vectors/predefined.spec.js.map +0 -1
@@ -1,755 +0,0 @@
1
- import { MatrixRotation2D, MatrixRotation3DRoll, MatrixRotation3DPitch, MatrixRotation3DYaw, MatrixRotation3D, MatrixRotation3DEulerAngles, MatrixScale2D, MatrixScale3D, MatrixTranslation2D, MatrixTranslation3D, MatrixTransform2D, MatrixTransform3D, MatrixDirection3D, MatrixView, MatrixPerspective, MatrixOrthographic, } from './transformations.js';
2
- import { DegreesToRadians } from '../angles.ts';
3
- import { AssertMatrixRow, AssertMatrixValue } from './asserts.js';
4
- import { MatrixIdentity } from './core.js';
5
- import { VectorMagnitude } from '../vectors/core.ts';
6
- describe('Matrix Transformations', () => {
7
- // Helper function to check if matrices are approximately equal
8
- function expectMatrixToBeCloseTo(actual, expected, precision = 5) {
9
- expect(actual.length).toBe(expected.length);
10
- for (let i = 0; i < actual.length; i++) {
11
- const actualRow = actual[i];
12
- expect(actualRow).toBeDefined();
13
- AssertMatrixRow(actualRow);
14
- const expectedRow = expected[i];
15
- expect(expectedRow).toBeDefined();
16
- AssertMatrixRow(expectedRow);
17
- const actualRowLength = actualRow.length;
18
- const expectedRowLength = expectedRow.length;
19
- expect(actualRowLength).toBe(expectedRowLength);
20
- for (let j = 0; j < actualRow.length; j++) {
21
- const actualValue = actualRow[j];
22
- AssertMatrixValue(actualValue);
23
- const expectedValue = expectedRow[j];
24
- AssertMatrixValue(expectedValue);
25
- expect(actualValue).toBeCloseTo(expectedValue, precision);
26
- }
27
- }
28
- }
29
- describe('2D Rotation', () => {
30
- describe('MatrixRotation2D', () => {
31
- test('should create identity matrix for 0 radians', () => {
32
- const matrix = MatrixRotation2D(0);
33
- expectMatrixToBeCloseTo(matrix, [
34
- [1, 0, 0],
35
- [0, 1, 0],
36
- [0, 0, 1],
37
- ]);
38
- });
39
- test('should create 90-degree rotation matrix', () => {
40
- const matrix = MatrixRotation2D(Math.PI / 2);
41
- expectMatrixToBeCloseTo(matrix, [
42
- [0, -1, 0],
43
- [1, 0, 0],
44
- [0, 0, 1],
45
- ]);
46
- });
47
- test('should create 180-degree rotation matrix', () => {
48
- const matrix = MatrixRotation2D(Math.PI);
49
- expectMatrixToBeCloseTo(matrix, [
50
- [-1, 0, 0],
51
- [0, -1, 0],
52
- [0, 0, 1],
53
- ]);
54
- });
55
- test('should create 270-degree rotation matrix', () => {
56
- const matrix = MatrixRotation2D(3 * Math.PI / 2);
57
- expectMatrixToBeCloseTo(matrix, [
58
- [0, 1, 0],
59
- [-1, 0, 0],
60
- [0, 0, 1],
61
- ]);
62
- });
63
- test('should handle negative angles', () => {
64
- const matrix = MatrixRotation2D(-Math.PI / 2);
65
- expectMatrixToBeCloseTo(matrix, [
66
- [0, 1, 0],
67
- [-1, 0, 0],
68
- [0, 0, 1],
69
- ]);
70
- });
71
- test('should throw for invalid input', () => {
72
- // @ts-expect-error Argument of type 'string' is not assignable to parameter of type 'number'. This should throw at runtime.
73
- expect(() => MatrixRotation2D('invalid')).toThrow('Rotation angle must be a number');
74
- expect(() => MatrixRotation2D(NaN)).toThrow('Rotation angle must be a number');
75
- expect(() => MatrixRotation2D(Infinity)).toThrow('Rotation angle must be a number');
76
- });
77
- });
78
- });
79
- describe('3D Rotations', () => {
80
- describe('MatrixRotation3DRoll', () => {
81
- test('should create identity matrix for 0 radians', () => {
82
- const matrix = MatrixRotation3DRoll(0);
83
- expectMatrixToBeCloseTo(matrix, [
84
- [1, 0, 0, 0],
85
- [0, 1, 0, 0],
86
- [0, 0, 1, 0],
87
- [0, 0, 0, 1],
88
- ]);
89
- });
90
- test('should create 90-degree roll rotation', () => {
91
- const matrix = MatrixRotation3DRoll(Math.PI / 2);
92
- expectMatrixToBeCloseTo(matrix, [
93
- [1, 0, 0, 0],
94
- [0, 0, -1, 0],
95
- [0, 1, 0, 0],
96
- [0, 0, 0, 1],
97
- ]);
98
- });
99
- test('should throw for invalid input', () => {
100
- expect(() => MatrixRotation3DRoll(NaN)).toThrow('Rotation angle must be a number');
101
- });
102
- });
103
- describe('MatrixRotation3DPitch', () => {
104
- test('should create identity matrix for 0 radians', () => {
105
- const matrix = MatrixRotation3DPitch(0);
106
- expectMatrixToBeCloseTo(matrix, [
107
- [1, 0, 0, 0],
108
- [0, 1, 0, 0],
109
- [0, 0, 1, 0],
110
- [0, 0, 0, 1],
111
- ]);
112
- });
113
- test('should create 90-degree pitch rotation', () => {
114
- const matrix = MatrixRotation3DPitch(Math.PI / 2);
115
- expectMatrixToBeCloseTo(matrix, [
116
- [0, 0, 1, 0],
117
- [0, 1, 0, 0],
118
- [-1, 0, 0, 0],
119
- [0, 0, 0, 1],
120
- ]);
121
- });
122
- test('should throw for invalid input', () => {
123
- expect(() => MatrixRotation3DPitch(NaN)).toThrow('Rotation angle must be a number');
124
- });
125
- });
126
- describe('MatrixRotation3DYaw', () => {
127
- test('should create identity matrix for 0 radians', () => {
128
- const matrix = MatrixRotation3DYaw(0);
129
- expectMatrixToBeCloseTo(matrix, [
130
- [1, 0, 0, 0],
131
- [0, 1, 0, 0],
132
- [0, 0, 1, 0],
133
- [0, 0, 0, 1],
134
- ]);
135
- });
136
- test('should create 90-degree yaw rotation', () => {
137
- const matrix = MatrixRotation3DYaw(Math.PI / 2);
138
- expectMatrixToBeCloseTo(matrix, [
139
- [0, -1, 0, 0],
140
- [1, 0, 0, 0],
141
- [0, 0, 1, 0],
142
- [0, 0, 0, 1],
143
- ]);
144
- });
145
- test('should throw for invalid input', () => {
146
- expect(() => MatrixRotation3DYaw(NaN)).toThrow('Rotation angle must be a number');
147
- });
148
- });
149
- describe('MatrixRotation3D', () => {
150
- test('should create identity matrix for zero rotation', () => {
151
- const matrix = MatrixRotation3D(0, 0, 0);
152
- expectMatrixToBeCloseTo(matrix, [
153
- [1, 0, 0, 0],
154
- [0, 1, 0, 0],
155
- [0, 0, 1, 0],
156
- [0, 0, 0, 1],
157
- ]);
158
- });
159
- test('should handle single axis rotations', () => {
160
- const rollMatrix = MatrixRotation3D(Math.PI / 2, 0, 0);
161
- const expectedRoll = MatrixRotation3DRoll(Math.PI / 2);
162
- expectMatrixToBeCloseTo(rollMatrix, expectedRoll);
163
- const pitchMatrix = MatrixRotation3D(0, Math.PI / 2, 0);
164
- const expectedPitch = MatrixRotation3DPitch(Math.PI / 2);
165
- expectMatrixToBeCloseTo(pitchMatrix, expectedPitch);
166
- const yawMatrix = MatrixRotation3D(0, 0, Math.PI / 2);
167
- const expectedYaw = MatrixRotation3DYaw(Math.PI / 2);
168
- expectMatrixToBeCloseTo(yawMatrix, expectedYaw);
169
- });
170
- test('should create complex rotation with all axes', () => {
171
- const matrix = MatrixRotation3D(DegreesToRadians(60), DegreesToRadians(30), DegreesToRadians(45));
172
- // Matrix should not be identity
173
- expect(matrix).not.toEqual([
174
- [1, 0, 0, 0],
175
- [0, 1, 0, 0],
176
- [0, 0, 1, 0],
177
- [0, 0, 0, 1],
178
- ]);
179
- // Should be a proper rotation matrix (orthogonal with determinant 1)
180
- // Basic check: bottom row should be [0, 0, 0, 1]
181
- expectMatrixToBeCloseTo([matrix[3]], [[0, 0, 0, 1]]);
182
- });
183
- test('should handle vector input', () => {
184
- const eulerAngles = [Math.PI / 4, Math.PI / 6, Math.PI / 3];
185
- const matrix = MatrixRotation3D(eulerAngles);
186
- const expected = MatrixRotation3D(Math.PI / 4, Math.PI / 6, Math.PI / 3);
187
- expectMatrixToBeCloseTo(matrix, expected);
188
- });
189
- });
190
- describe('MatrixRotation3DEulerAngles', () => {
191
- test('should convert degrees to radians and create rotation matrix', () => {
192
- const matrix = MatrixRotation3DEulerAngles(60, 30, 45);
193
- const expected = MatrixRotation3D(DegreesToRadians(60), DegreesToRadians(30), DegreesToRadians(45));
194
- expectMatrixToBeCloseTo(matrix, expected);
195
- });
196
- test('should handle zero degrees', () => {
197
- const matrix = MatrixRotation3DEulerAngles(0, 0, 0);
198
- expectMatrixToBeCloseTo(matrix, [
199
- [1, 0, 0, 0],
200
- [0, 1, 0, 0],
201
- [0, 0, 1, 0],
202
- [0, 0, 0, 1],
203
- ]);
204
- });
205
- });
206
- });
207
- describe('Scaling Transformations', () => {
208
- describe('Matrix_Transformation_Scale2D', () => {
209
- test('should create identity scale for (1, 1)', () => {
210
- const matrix = MatrixScale2D(1, 1);
211
- expectMatrixToBeCloseTo(matrix, [
212
- [1, 0, 0],
213
- [0, 1, 0],
214
- [0, 0, 1],
215
- ]);
216
- });
217
- test('should create proper scale matrix', () => {
218
- const matrix = MatrixScale2D(2, 3);
219
- expectMatrixToBeCloseTo(matrix, [
220
- [2, 0, 0],
221
- [0, 3, 0],
222
- [0, 0, 1],
223
- ]);
224
- });
225
- test('should handle negative scaling (flipping)', () => {
226
- const matrix = MatrixScale2D(-1, 1);
227
- expectMatrixToBeCloseTo(matrix, [
228
- [-1, 0, 0],
229
- [0, 1, 0],
230
- [0, 0, 1],
231
- ]);
232
- });
233
- test('should handle zero scaling', () => {
234
- const matrix = MatrixScale2D(0, 1);
235
- expectMatrixToBeCloseTo(matrix, [
236
- [0, 0, 0],
237
- [0, 1, 0],
238
- [0, 0, 1],
239
- ]);
240
- });
241
- });
242
- describe('Matrix_Transformation_Scale3D', () => {
243
- test('should create identity scale for (1, 1, 1)', () => {
244
- const matrix = MatrixScale3D(1, 1, 1);
245
- expectMatrixToBeCloseTo(matrix, [
246
- [1, 0, 0, 0],
247
- [0, 1, 0, 0],
248
- [0, 0, 1, 0],
249
- [0, 0, 0, 1],
250
- ]);
251
- });
252
- test('should create proper 3D scale matrix', () => {
253
- const matrix = MatrixScale3D(2, 3, 4);
254
- expectMatrixToBeCloseTo(matrix, [
255
- [2, 0, 0, 0],
256
- [0, 3, 0, 0],
257
- [0, 0, 4, 0],
258
- [0, 0, 0, 1],
259
- ]);
260
- });
261
- test('should handle negative scaling', () => {
262
- const matrix = MatrixScale3D(-1, -1, -1);
263
- expectMatrixToBeCloseTo(matrix, [
264
- [-1, 0, 0, 0],
265
- [0, -1, 0, 0],
266
- [0, 0, -1, 0],
267
- [0, 0, 0, 1],
268
- ]);
269
- });
270
- });
271
- });
272
- describe('Scale Transformations', () => {
273
- describe('MatrixScale2D', () => {
274
- test('should create uniform scale matrix', () => {
275
- const matrix = MatrixScale2D(2);
276
- expectMatrixToBeCloseTo(matrix, [
277
- [2, 0, 0],
278
- [0, 2, 0],
279
- [0, 0, 1],
280
- ]);
281
- });
282
- test('should create identity for scale 1', () => {
283
- const matrix = MatrixScale2D(1);
284
- expectMatrixToBeCloseTo(matrix, [
285
- [1, 0, 0],
286
- [0, 1, 0],
287
- [0, 0, 1],
288
- ]);
289
- });
290
- test('should create independent scale matrix', () => {
291
- const matrix = MatrixScale2D(2, 3);
292
- expectMatrixToBeCloseTo(matrix, [
293
- [2, 0, 0],
294
- [0, 3, 0],
295
- [0, 0, 1],
296
- ]);
297
- });
298
- test('should handle vector input', () => {
299
- const scaleVector = [2, 3];
300
- const matrix = MatrixScale2D(scaleVector);
301
- expectMatrixToBeCloseTo(matrix, [
302
- [2, 0, 0],
303
- [0, 3, 0],
304
- [0, 0, 1],
305
- ]);
306
- });
307
- test('should handle negative scaling (flipping)', () => {
308
- const matrix = MatrixScale2D(-1, 1);
309
- expectMatrixToBeCloseTo(matrix, [
310
- [-1, 0, 0],
311
- [0, 1, 0],
312
- [0, 0, 1],
313
- ]);
314
- });
315
- });
316
- describe('MatrixScale3D', () => {
317
- test('should create uniform scale matrix', () => {
318
- const matrix = MatrixScale3D(2);
319
- expectMatrixToBeCloseTo(matrix, [
320
- [2, 0, 0, 0],
321
- [0, 2, 0, 0],
322
- [0, 0, 2, 0],
323
- [0, 0, 0, 1],
324
- ]);
325
- });
326
- test('should create identity for scale 1', () => {
327
- const matrix = MatrixScale3D(1);
328
- expectMatrixToBeCloseTo(matrix, [
329
- [1, 0, 0, 0],
330
- [0, 1, 0, 0],
331
- [0, 0, 1, 0],
332
- [0, 0, 0, 1],
333
- ]);
334
- });
335
- test('should create independent scale matrix', () => {
336
- const matrix = MatrixScale3D(2, 3, 4);
337
- expectMatrixToBeCloseTo(matrix, [
338
- [2, 0, 0, 0],
339
- [0, 3, 0, 0],
340
- [0, 0, 4, 0],
341
- [0, 0, 0, 1],
342
- ]);
343
- });
344
- test('should handle vector input', () => {
345
- const scaleVector = [2, 3, 4];
346
- const matrix = MatrixScale3D(scaleVector);
347
- expectMatrixToBeCloseTo(matrix, [
348
- [2, 0, 0, 0],
349
- [0, 3, 0, 0],
350
- [0, 0, 4, 0],
351
- [0, 0, 0, 1],
352
- ]);
353
- });
354
- });
355
- });
356
- describe('Translation Transformations', () => {
357
- describe('MatrixTranslation2D', () => {
358
- test('should create identity for zero translation', () => {
359
- const matrix = MatrixTranslation2D(0, 0);
360
- expectMatrixToBeCloseTo(matrix, [
361
- [1, 0, 0],
362
- [0, 1, 0],
363
- [0, 0, 1],
364
- ]);
365
- });
366
- test('should create proper translation matrix', () => {
367
- const matrix = MatrixTranslation2D(5, 7);
368
- expectMatrixToBeCloseTo(matrix, [
369
- [1, 0, 5],
370
- [0, 1, 7],
371
- [0, 0, 1],
372
- ]);
373
- });
374
- test('should handle negative translation', () => {
375
- const matrix = MatrixTranslation2D(-3, -4);
376
- expectMatrixToBeCloseTo(matrix, [
377
- [1, 0, -3],
378
- [0, 1, -4],
379
- [0, 0, 1],
380
- ]);
381
- });
382
- });
383
- describe('MatrixTranslation3D', () => {
384
- test('should create identity for zero translation', () => {
385
- const matrix = MatrixTranslation3D(0, 0, 0);
386
- expectMatrixToBeCloseTo(matrix, [
387
- [1, 0, 0, 0],
388
- [0, 1, 0, 0],
389
- [0, 0, 1, 0],
390
- [0, 0, 0, 1],
391
- ]);
392
- });
393
- test('should create proper 3D translation matrix', () => {
394
- const matrix = MatrixTranslation3D(1, 2, 3);
395
- expectMatrixToBeCloseTo(matrix, [
396
- [1, 0, 0, 1],
397
- [0, 1, 0, 2],
398
- [0, 0, 1, 3],
399
- [0, 0, 0, 1],
400
- ]);
401
- });
402
- test('should handle uniform translation', () => {
403
- const matrix = MatrixTranslation3D(5);
404
- expectMatrixToBeCloseTo(matrix, [
405
- [1, 0, 0, 5],
406
- [0, 1, 0, 5],
407
- [0, 0, 1, 5],
408
- [0, 0, 0, 1],
409
- ]);
410
- });
411
- test('should handle vector input', () => {
412
- const translationVector = [1, 2, 3];
413
- const matrix = MatrixTranslation3D(translationVector[0], translationVector[1], translationVector[2]);
414
- expectMatrixToBeCloseTo(matrix, [
415
- [1, 0, 0, 1],
416
- [0, 1, 0, 2],
417
- [0, 0, 1, 3],
418
- [0, 0, 0, 1],
419
- ]);
420
- });
421
- test('should handle negative translation', () => {
422
- const matrix = MatrixTranslation3D(-5, -10, -15);
423
- expectMatrixToBeCloseTo(matrix, [
424
- [1, 0, 0, -5],
425
- [0, 1, 0, -10],
426
- [0, 0, 1, -15],
427
- [0, 0, 0, 1],
428
- ]);
429
- });
430
- });
431
- });
432
- describe('Vector Transformations', () => {
433
- describe('MatrixTransform2D', () => {
434
- test('should transform vector with identity matrix', () => {
435
- const identity = MatrixIdentity(3);
436
- const vector = [3, 4];
437
- const result = MatrixTransform2D(vector, identity);
438
- expect(result[0]).toBeCloseTo(3);
439
- expect(result[1]).toBeCloseTo(4);
440
- });
441
- test('should transform vector with translation', () => {
442
- const translation = MatrixTranslation2D(2, 3);
443
- const vector = [1, 1];
444
- const result = MatrixTransform2D(vector, translation);
445
- expect(result[0]).toBeCloseTo(3);
446
- expect(result[1]).toBeCloseTo(4);
447
- });
448
- test('should transform vector with rotation', () => {
449
- const rotation = MatrixRotation2D(Math.PI / 2);
450
- const vector = [1, 0];
451
- const result = MatrixTransform2D(vector, rotation);
452
- expect(result[0]).toBeCloseTo(0, 5);
453
- expect(result[1]).toBeCloseTo(1, 5);
454
- });
455
- test('should transform vector with scaling', () => {
456
- const scale = MatrixScale2D(2, 3);
457
- const vector = [4, 5];
458
- const result = MatrixTransform2D(vector, scale);
459
- expect(result[0]).toBeCloseTo(8);
460
- expect(result[1]).toBeCloseTo(15);
461
- });
462
- test('should throw for invalid matrix', () => {
463
- const invalidMatrix = [[1, 0], [0, 1]]; // Wrong size
464
- // @ts-expect-error Invalid matrix size should cause a type error: MatrixTransform2D expects a 3x3 matrix
465
- expect(() => MatrixTransform2D([1, 1], invalidMatrix)).toThrow();
466
- });
467
- test('should throw for invalid vector', () => {
468
- const identity = MatrixIdentity(3);
469
- expect(() => MatrixTransform2D([1], identity)).toThrow();
470
- });
471
- test('should throw for degenerate transformation', () => {
472
- const degenerate = [
473
- [1, 0, 0],
474
- [0, 1, 0],
475
- [0, 0, 0], // w = 0 causes division by zero
476
- ];
477
- expect(() => MatrixTransform2D([1, 1], degenerate)).toThrow('2D transformation w component near zero');
478
- });
479
- });
480
- describe('MatrixTransform3D', () => {
481
- test('should transform vector with identity matrix', () => {
482
- const identity = MatrixIdentity(4);
483
- const vector = [3, 4, 5];
484
- const result = MatrixTransform3D(vector, identity);
485
- expect(result[0]).toBeCloseTo(3);
486
- expect(result[1]).toBeCloseTo(4);
487
- expect(result[2]).toBeCloseTo(5);
488
- });
489
- test('should transform vector with translation', () => {
490
- const translation = MatrixTranslation3D(1, 2, 3);
491
- const vector = [4, 5, 6];
492
- const result = MatrixTransform3D(vector, translation);
493
- expect(result[0]).toBeCloseTo(5);
494
- expect(result[1]).toBeCloseTo(7);
495
- expect(result[2]).toBeCloseTo(9);
496
- });
497
- test('should transform vector with rotation', () => {
498
- const rotation = MatrixRotation3DYaw(Math.PI / 2);
499
- const vector = [1, 0, 0];
500
- const result = MatrixTransform3D(vector, rotation);
501
- expect(result[0]).toBeCloseTo(0, 5);
502
- expect(result[1]).toBeCloseTo(1, 5);
503
- expect(result[2]).toBeCloseTo(0, 5);
504
- });
505
- test('should transform vector with scaling', () => {
506
- const scale = MatrixScale3D(2, 3, 4);
507
- const vector = [1, 2, 3];
508
- const result = MatrixTransform3D(vector, scale);
509
- expect(result[0]).toBeCloseTo(2);
510
- expect(result[1]).toBeCloseTo(6);
511
- expect(result[2]).toBeCloseTo(12);
512
- });
513
- test('should throw for invalid matrix', () => {
514
- const invalidMatrix = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]; // Wrong size
515
- // @ts-expect-error Invalid matrix size should cause a type error: MatrixTransform3D expects a 4x4 matrix
516
- expect(() => MatrixTransform3D([1, 1, 1], invalidMatrix)).toThrow();
517
- });
518
- test('should throw for invalid vector', () => {
519
- const identity = MatrixIdentity(4);
520
- expect(() => MatrixTransform3D([1, 1], identity)).toThrow();
521
- });
522
- test('should throw for degenerate transformation', () => {
523
- const degenerate = [
524
- [1, 0, 0, 0],
525
- [0, 1, 0, 0],
526
- [0, 0, 1, 0],
527
- [0, 0, 0, 0], // w = 0 causes division by zero
528
- ];
529
- expect(() => MatrixTransform3D([1, 1, 1], degenerate)).toThrow('3D transformation w component near zero');
530
- });
531
- });
532
- describe('MatrixDirection3D', () => {
533
- test('should transform direction with identity matrix', () => {
534
- const identity = MatrixIdentity(3);
535
- const direction = [1, 0, 0];
536
- const result = MatrixDirection3D(direction, identity);
537
- expect(result[0]).toBeCloseTo(1);
538
- expect(result[1]).toBeCloseTo(0);
539
- expect(result[2]).toBeCloseTo(0);
540
- });
541
- test('should transform direction with rotation', () => {
542
- // Extract 3x3 rotation part from 4x4 matrix
543
- const yaw4x4 = MatrixRotation3DYaw(Math.PI / 2);
544
- const rotation3x3 = [
545
- [yaw4x4[0][0], yaw4x4[0][1], yaw4x4[0][2]],
546
- [yaw4x4[1][0], yaw4x4[1][1], yaw4x4[1][2]],
547
- [yaw4x4[2][0], yaw4x4[2][1], yaw4x4[2][2]],
548
- ];
549
- const direction = [1, 0, 0];
550
- const result = MatrixDirection3D(direction, rotation3x3);
551
- expect(result[0]).toBeCloseTo(0, 5);
552
- expect(result[1]).toBeCloseTo(1, 5);
553
- expect(result[2]).toBeCloseTo(0, 5);
554
- });
555
- test('should ignore translation (direction vectors should not be translated)', () => {
556
- // Create a 3x3 matrix that would translate if it were 4x4
557
- const transform = [
558
- [1, 0, 5], // Translation component in 3x3 context
559
- [0, 1, 10],
560
- [0, 0, 1],
561
- ];
562
- const direction = [1, 1, 0];
563
- const result = MatrixDirection3D(direction, transform);
564
- // Should apply transformation but not translation effects
565
- expect(result[0]).toBeCloseTo(1);
566
- expect(result[1]).toBeCloseTo(1);
567
- expect(result[2]).toBeCloseTo(0);
568
- });
569
- test('should throw for invalid matrix', () => {
570
- const invalidMatrix = [[1, 0], [0, 1]]; // Wrong size
571
- // @ts-expect-error Invalid matrix size should cause a type error: MatrixDirection3D expects a 3x3 matrix
572
- expect(() => MatrixDirection3D([1, 1, 1], invalidMatrix)).toThrow();
573
- });
574
- test('should throw for invalid direction', () => {
575
- const identity = [
576
- [1, 0, 0],
577
- [0, 1, 0],
578
- [0, 0, 1],
579
- ];
580
- expect(() => MatrixDirection3D([1, 1], identity)).toThrow();
581
- });
582
- });
583
- });
584
- describe('View and Projection Matrices', () => {
585
- describe('MatrixView', () => {
586
- test('should create view matrix looking down negative Z', () => {
587
- const eye = [0, 0, 0];
588
- const target = [0, 0, -1];
589
- const up = [0, 1, 0];
590
- const viewMatrix = MatrixView(eye, target, up);
591
- // Should be approximately identity since we're at origin looking down -Z
592
- expectMatrixToBeCloseTo(viewMatrix, [
593
- [1, 0, 0, 0],
594
- [0, 1, 0, 0],
595
- [0, 0, 1, 0],
596
- [0, 0, 0, 1],
597
- ]);
598
- });
599
- test('should create view matrix with translation', () => {
600
- const eye = [0, 0, 5];
601
- const target = [0, 0, 0];
602
- const up = [0, 1, 0];
603
- const viewMatrix = MatrixView(eye, target, up);
604
- // Should translate camera position
605
- expect(viewMatrix[0][3]).toBeCloseTo(0);
606
- expect(viewMatrix[1][3]).toBeCloseTo(0);
607
- expect(viewMatrix[2][3]).toBeCloseTo(-5);
608
- expect(viewMatrix[3][3]).toBeCloseTo(1);
609
- });
610
- test('should create view matrix with rotation', () => {
611
- const eye = [1, 0, 0];
612
- const target = [0, 0, 0];
613
- const up = [0, 1, 0];
614
- const viewMatrix = MatrixView(eye, target, up);
615
- // Camera is to the right of origin looking towards it
616
- // This should create a rotation
617
- expect(viewMatrix).not.toEqual([
618
- [1, 0, 0, 0],
619
- [0, 1, 0, 0],
620
- [0, 0, 1, 0],
621
- [0, 0, 0, 1],
622
- ]);
623
- // Bottom row should always be [0, 0, 0, 1]
624
- expectMatrixToBeCloseTo([viewMatrix[3]], [[0, 0, 0, 1]]);
625
- });
626
- test('should throw for invalid vectors', () => {
627
- expect(() => MatrixView([1, 2], [0, 0, 0], [0, 1, 0])).toThrow();
628
- expect(() => MatrixView([0, 0, 0], [1, 2], [0, 1, 0])).toThrow();
629
- expect(() => MatrixView([0, 0, 0], [0, 0, 0], [1, 2])).toThrow();
630
- });
631
- });
632
- describe('Matrix_Transformation_Perspective', () => {
633
- test('should create perspective matrix with valid parameters', () => {
634
- const fovY = Math.PI / 4; // 45 degrees
635
- const aspect = 16 / 9;
636
- const near = 0.1;
637
- const far = 100;
638
- const perspective = MatrixPerspective(fovY, aspect, near, far);
639
- // Should have specific structure for perspective matrix
640
- expect(perspective[0][0]).toBeGreaterThan(0); // X scaling
641
- expect(perspective[1][1]).toBeGreaterThan(0); // Y scaling
642
- expect(perspective[2][2]).toBeLessThan(0); // Z mapping (negative)
643
- expect(perspective[2][3]).toBeLessThan(0); // Z translation (negative)
644
- expect(perspective[3][2]).toBe(-1); // Perspective trigger
645
- expect(perspective[3][3]).toBe(0); // Clear diagonal
646
- });
647
- test('should throw for invalid near plane', () => {
648
- expect(() => MatrixPerspective(Math.PI / 4, 1, 0, 100)).toThrow('Near clipping plane must be greater than 0');
649
- expect(() => MatrixPerspective(Math.PI / 4, 1, -1, 100)).toThrow('Near clipping plane must be greater than 0');
650
- });
651
- test('should throw for invalid far plane', () => {
652
- expect(() => MatrixPerspective(Math.PI / 4, 1, 1, 0)).toThrow('Far clipping plane must be greater than 0');
653
- expect(() => MatrixPerspective(Math.PI / 4, 1, 1, -1)).toThrow('Far clipping plane must be greater than 0');
654
- });
655
- test('should throw when near >= far', () => {
656
- expect(() => MatrixPerspective(Math.PI / 4, 1, 100, 100)).toThrow('Near clipping plane must be less than far clipping plane');
657
- expect(() => MatrixPerspective(Math.PI / 4, 1, 100, 50)).toThrow('Near clipping plane must be less than far clipping plane');
658
- });
659
- test('should throw for invalid aspect ratio', () => {
660
- expect(() => MatrixPerspective(Math.PI / 4, 0, 0.1, 100)).toThrow('Aspect ratio must be greater than 0');
661
- expect(() => MatrixPerspective(Math.PI / 4, -1, 0.1, 100)).toThrow('Aspect ratio must be greater than 0');
662
- });
663
- });
664
- describe('MatrixOrthographic', () => {
665
- test('should create orthographic matrix with valid parameters', () => {
666
- const left = -10;
667
- const right = 10;
668
- const bottom = -5;
669
- const top = 5;
670
- const near = -100;
671
- const far = 100;
672
- const ortho = MatrixOrthographic(left, right, bottom, top, near, far);
673
- // Should have specific structure for orthographic matrix
674
- expect(ortho[0][0]).toBeCloseTo(2 / (right - left)); // X scaling
675
- expect(ortho[1][1]).toBeCloseTo(2 / (top - bottom)); // Y scaling
676
- expect(ortho[2][2]).toBeCloseTo(-2 / (far - near)); // Z scaling (negative)
677
- expect(ortho[0][3]).toBeCloseTo(-(right + left) / (right - left)); // X translation
678
- expect(ortho[1][3]).toBeCloseTo(-(top + bottom) / (top - bottom)); // Y translation
679
- expect(ortho[2][3]).toBeCloseTo(-(far + near) / (far - near)); // Z translation
680
- expect(ortho[3][3]).toBe(1); // Homogeneous coordinate
681
- });
682
- test('should throw for equal boundaries', () => {
683
- expect(() => MatrixOrthographic(5, 5, -5, 5, -100, 100)).toThrow('Left and right bounds must not be equal');
684
- expect(() => MatrixOrthographic(-5, 5, 3, 3, -100, 100)).toThrow('Bottom and top bounds must not be equal');
685
- expect(() => MatrixOrthographic(-5, 5, -5, 5, 50, 50)).toThrow('Near and far clipping planes must not be equal');
686
- });
687
- test('should handle negative coordinate spaces', () => {
688
- const ortho = MatrixOrthographic(-100, -50, -25, -10, 10, 20);
689
- // Should still create valid matrix
690
- expect(ortho[3][3]).toBe(1);
691
- expect(ortho[0][0]).toBeCloseTo(2 / (-50 - -100));
692
- expect(ortho[1][1]).toBeCloseTo(2 / (-10 - -25));
693
- expect(ortho[2][2]).toBeCloseTo(-2 / (20 - 10));
694
- });
695
- });
696
- });
697
- describe('Integration Tests', () => {
698
- test('should compose transformations correctly', () => {
699
- // Create a composite transformation: translate, then rotate, then scale
700
- const translation = MatrixTranslation2D(5, 5);
701
- const rotation = MatrixRotation2D(Math.PI / 4); // 45 degrees
702
- const scale = MatrixScale2D(2, 2);
703
- // Apply transformations in sequence
704
- const point = [1, 0];
705
- // First translate
706
- const translated = MatrixTransform2D(point, translation);
707
- expect(translated[0]).toBeCloseTo(6);
708
- expect(translated[1]).toBeCloseTo(5);
709
- // Then rotate
710
- const rotated = MatrixTransform2D(translated, rotation);
711
- // Then scale
712
- const final = MatrixTransform2D(rotated, scale);
713
- // Final result should be properly transformed
714
- expect(final).toBeDefined();
715
- expect(Array.isArray(final)).toBe(true);
716
- expect(final.length).toBe(2);
717
- });
718
- test('should maintain vector magnitude for rotation only', () => {
719
- const vector = [3, 4]; // magnitude = 5
720
- const rotation = MatrixRotation2D(Math.PI / 3); // 60 degrees
721
- const rotated = MatrixTransform2D(vector, rotation);
722
- const originalMagnitude = VectorMagnitude(vector);
723
- const rotatedMagnitude = VectorMagnitude(rotated);
724
- expect(rotatedMagnitude).toBeCloseTo(originalMagnitude, 5);
725
- });
726
- test('should transform 3D vectors correctly with combined rotations', () => {
727
- const vector = [1, 0, 0];
728
- // First rotate around Y (pitch), then around Z (yaw)
729
- const pitch90 = MatrixRotation3DPitch(Math.PI / 2);
730
- const yaw90 = MatrixRotation3DYaw(Math.PI / 2);
731
- const afterPitch = MatrixTransform3D(vector, pitch90);
732
- expect(afterPitch[0]).toBeCloseTo(0, 5);
733
- expect(afterPitch[1]).toBeCloseTo(0, 5);
734
- expect(afterPitch[2]).toBeCloseTo(-1, 5);
735
- const afterYaw = MatrixTransform3D(afterPitch, yaw90);
736
- expect(afterYaw[0]).toBeCloseTo(0, 5);
737
- expect(afterYaw[1]).toBeCloseTo(0, 5);
738
- expect(afterYaw[2]).toBeCloseTo(-1, 5);
739
- });
740
- test('should preserve direction vector properties', () => {
741
- const direction = [1, 1, 1];
742
- const rotation3x3 = [
743
- [1, 0, 0],
744
- [0, 0, -1],
745
- [0, 1, 0],
746
- ];
747
- const rotatedDirection = MatrixDirection3D(direction, rotation3x3);
748
- const originalMagnitude = VectorMagnitude(direction);
749
- const rotatedMagnitude = VectorMagnitude(rotatedDirection);
750
- // Direction transformations should preserve magnitude
751
- expect(rotatedMagnitude).toBeCloseTo(originalMagnitude, 5);
752
- });
753
- });
754
- });
755
- //# sourceMappingURL=transformations.spec.js.map