@@ -107,6 +107,393 @@ class MatrixMathHelperTest {
107
107
verifyRotatedMatrix(10.0 , - 95.0 , 0.0 , - 170.0 , - 85.0 , - 180.0 )
108
108
}
109
109
110
+ @Test
111
+ fun testMultiplyInto () {
112
+ val matrixA =
113
+ doubleArrayOf(
114
+ 1.0 , 2.0 , 3.0 , 4.0 , 5.0 , 6.0 , 7.0 , 8.0 , 9.0 , 10.0 , 11.0 , 12.0 , 13.0 , 14.0 , 15.0 , 16.0 )
115
+ val matrixB =
116
+ doubleArrayOf(
117
+ 2.0 , 0.0 , 0.0 , 0.0 , 0.0 , 2.0 , 0.0 , 0.0 , 0.0 , 0.0 , 2.0 , 0.0 , 0.0 , 0.0 , 0.0 , 2.0 )
118
+ val result = DoubleArray (16 )
119
+
120
+ MatrixMathHelper .multiplyInto(result, matrixA, matrixB)
121
+
122
+ val expected =
123
+ doubleArrayOf(
124
+ 2.0 ,
125
+ 4.0 ,
126
+ 6.0 ,
127
+ 8.0 ,
128
+ 10.0 ,
129
+ 12.0 ,
130
+ 14.0 ,
131
+ 16.0 ,
132
+ 18.0 ,
133
+ 20.0 ,
134
+ 22.0 ,
135
+ 24.0 ,
136
+ 26.0 ,
137
+ 28.0 ,
138
+ 30.0 ,
139
+ 32.0 )
140
+
141
+ assertThat(result).containsExactly(* expected)
142
+ }
143
+
144
+ @Test
145
+ fun testCreateIdentityMatrix () {
146
+ val identity = MatrixMathHelper .createIdentityMatrix()
147
+
148
+ val expected =
149
+ doubleArrayOf(
150
+ 1.0 , 0.0 , 0.0 , 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 0.0 , 0.0 , 1.0 )
151
+
152
+ assertThat(identity).containsExactly(* expected)
153
+ }
154
+
155
+ @Test
156
+ fun testResetIdentityMatrix () {
157
+ val matrix =
158
+ doubleArrayOf(
159
+ 5.0 , 2.0 , 3.0 , 4.0 , 1.0 , 6.0 , 7.0 , 8.0 , 9.0 , 10.0 , 11.0 , 12.0 , 13.0 , 14.0 , 15.0 , 16.0 )
160
+
161
+ MatrixMathHelper .resetIdentityMatrix(matrix)
162
+
163
+ val expected =
164
+ doubleArrayOf(
165
+ 1.0 , 0.0 , 0.0 , 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 0.0 , 0.0 , 1.0 )
166
+
167
+ assertThat(matrix).containsExactly(* expected)
168
+ }
169
+
170
+ @Test
171
+ fun testDeterminant () {
172
+ val identityMatrix = MatrixMathHelper .createIdentityMatrix()
173
+ assertThat(MatrixMathHelper .determinant(identityMatrix)).isEqualTo(1.0 )
174
+
175
+ val matrix =
176
+ doubleArrayOf(
177
+ 2.0 , 0.0 , 0.0 , 0.0 , 0.0 , 2.0 , 0.0 , 0.0 , 0.0 , 0.0 , 2.0 , 0.0 , 0.0 , 0.0 , 0.0 , 2.0 )
178
+ assertThat(MatrixMathHelper .determinant(matrix)).isEqualTo(16.0 )
179
+ }
180
+
181
+ @Test
182
+ fun testInverse () {
183
+ val matrix =
184
+ doubleArrayOf(
185
+ 2.0 , 0.0 , 0.0 , 0.0 , 0.0 , 2.0 , 0.0 , 0.0 , 0.0 , 0.0 , 2.0 , 0.0 , 0.0 , 0.0 , 0.0 , 2.0 )
186
+
187
+ val inverse = MatrixMathHelper .inverse(matrix)
188
+
189
+ val expected =
190
+ doubleArrayOf(
191
+ 0.5 , 0.0 , 0.0 , 0.0 , 0.0 , 0.5 , 0.0 , 0.0 , 0.0 , 0.0 , 0.5 , 0.0 , 0.0 , 0.0 , 0.0 , 0.5 )
192
+
193
+ assertThat(inverse).containsExactly(* expected)
194
+ }
195
+
196
+ @Test
197
+ fun testTranspose () {
198
+ val matrix =
199
+ doubleArrayOf(
200
+ 1.0 , 2.0 , 3.0 , 4.0 , 5.0 , 6.0 , 7.0 , 8.0 , 9.0 , 10.0 , 11.0 , 12.0 , 13.0 , 14.0 , 15.0 , 16.0 )
201
+
202
+ val transposed = MatrixMathHelper .transpose(matrix)
203
+
204
+ val expected =
205
+ doubleArrayOf(
206
+ 1.0 , 5.0 , 9.0 , 13.0 , 2.0 , 6.0 , 10.0 , 14.0 , 3.0 , 7.0 , 11.0 , 15.0 , 4.0 , 8.0 , 12.0 , 16.0 )
207
+
208
+ assertThat(transposed).containsExactly(* expected)
209
+ }
210
+
211
+ @Test
212
+ fun testMultiplyVectorByMatrix () {
213
+ val vector = doubleArrayOf(1.0 , 2.0 , 3.0 , 1.0 )
214
+ val matrix =
215
+ doubleArrayOf(
216
+ 2.0 , 0.0 , 0.0 , 0.0 , 0.0 , 2.0 , 0.0 , 0.0 , 0.0 , 0.0 , 2.0 , 0.0 , 0.0 , 0.0 , 0.0 , 1.0 )
217
+ val result = DoubleArray (4 )
218
+
219
+ MatrixMathHelper .multiplyVectorByMatrix(vector, matrix, result)
220
+
221
+ val expected = doubleArrayOf(2.0 , 4.0 , 6.0 , 1.0 )
222
+ assertThat(result).containsExactly(* expected)
223
+ }
224
+
225
+ @Test
226
+ fun testV3Length () {
227
+ val vector = doubleArrayOf(3.0 , 4.0 , 0.0 )
228
+ val length = MatrixMathHelper .v3Length(vector)
229
+ assertThat(length).isEqualTo(5.0 )
230
+
231
+ val unitVector = doubleArrayOf(1.0 , 0.0 , 0.0 )
232
+ assertThat(MatrixMathHelper .v3Length(unitVector)).isEqualTo(1.0 )
233
+ }
234
+
235
+ @Test
236
+ fun testV3Normalize () {
237
+ val vector = doubleArrayOf(3.0 , 4.0 , 0.0 )
238
+ val norm = MatrixMathHelper .v3Length(vector)
239
+
240
+ val result = MatrixMathHelper .v3Normalize(vector, norm)
241
+
242
+ val expected = doubleArrayOf(0.6 , 0.8 , 0.0 )
243
+ assertThat(result[0 ]).isCloseTo(expected[0 ], org.assertj.core.data.Offset .offset(1e- 10 ))
244
+ assertThat(result[1 ]).isCloseTo(expected[1 ], org.assertj.core.data.Offset .offset(1e- 10 ))
245
+ assertThat(result[2 ]).isCloseTo(expected[2 ], org.assertj.core.data.Offset .offset(1e- 10 ))
246
+
247
+ // Verify the normalized vector has length 1
248
+ assertThat(MatrixMathHelper .v3Length(result))
249
+ .isCloseTo(1.0 , org.assertj.core.data.Offset .offset(1e- 10 ))
250
+ }
251
+
252
+ @Test
253
+ fun testV3Dot () {
254
+ val vectorA = doubleArrayOf(1.0 , 2.0 , 3.0 )
255
+ val vectorB = doubleArrayOf(4.0 , 5.0 , 6.0 )
256
+
257
+ val dotProduct = MatrixMathHelper .v3Dot(vectorA, vectorB)
258
+
259
+ // 1*4 + 2*5 + 3*6 = 4 + 10 + 18 = 32
260
+ assertThat(dotProduct).isEqualTo(32.0 )
261
+
262
+ // Test orthogonal vectors
263
+ val orthogonalA = doubleArrayOf(1.0 , 0.0 , 0.0 )
264
+ val orthogonalB = doubleArrayOf(0.0 , 1.0 , 0.0 )
265
+ assertThat(MatrixMathHelper .v3Dot(orthogonalA, orthogonalB)).isEqualTo(0.0 )
266
+ }
267
+
268
+ @Test
269
+ fun testV3Combine () {
270
+ val vectorA = doubleArrayOf(1.0 , 2.0 , 3.0 )
271
+ val vectorB = doubleArrayOf(4.0 , 5.0 , 6.0 )
272
+
273
+ val result = MatrixMathHelper .v3Combine(vectorA, vectorB, 2.0 , 3.0 )
274
+
275
+ // result = 2*vectorA + 3*vectorB = 2*(1,2,3) + 3*(4,5,6) = (2,4,6) + (12,15,18) = (14,19,24)
276
+ val expected = doubleArrayOf(14.0 , 19.0 , 24.0 )
277
+ assertThat(result).containsExactly(* expected)
278
+ }
279
+
280
+ @Test
281
+ fun testV3Cross () {
282
+ val vectorA = doubleArrayOf(1.0 , 0.0 , 0.0 )
283
+ val vectorB = doubleArrayOf(0.0 , 1.0 , 0.0 )
284
+
285
+ val result = MatrixMathHelper .v3Cross(vectorA, vectorB)
286
+
287
+ // Cross product of (1,0,0) x (0,1,0) = (0,0,1)
288
+ val expected = doubleArrayOf(0.0 , 0.0 , 1.0 )
289
+ assertThat(result).containsExactly(* expected)
290
+
291
+ // Test another cross product: (1,2,3) x (4,5,6)
292
+ val vectorC = doubleArrayOf(1.0 , 2.0 , 3.0 )
293
+ val vectorD = doubleArrayOf(4.0 , 5.0 , 6.0 )
294
+
295
+ val result2 = MatrixMathHelper .v3Cross(vectorC, vectorD)
296
+
297
+ // (1,2,3) x (4,5,6) = (2*6-3*5, 3*4-1*6, 1*5-2*4) = (12-15, 12-6, 5-8) = (-3,6,-3)
298
+ val expected2 = doubleArrayOf(- 3.0 , 6.0 , - 3.0 )
299
+ assertThat(result2).containsExactly(* expected2)
300
+ }
301
+
302
+ @Test
303
+ fun testRoundTo3Places () {
304
+ assertThat(MatrixMathHelper .roundTo3Places(1.23456789 )).isEqualTo(1.235 )
305
+ assertThat(MatrixMathHelper .roundTo3Places(1.2344 )).isEqualTo(1.234 )
306
+ assertThat(MatrixMathHelper .roundTo3Places(1.0 )).isEqualTo(1.0 )
307
+ assertThat(MatrixMathHelper .roundTo3Places(- 1.23456789 )).isEqualTo(- 1.235 )
308
+ }
309
+
310
+ @Test
311
+ fun testDegreesToRadians () {
312
+ assertThat(MatrixMathHelper .degreesToRadians(0.0 )).isEqualTo(0.0 )
313
+ assertThat(MatrixMathHelper .degreesToRadians(90.0 ))
314
+ .isCloseTo(Math .PI / 2 , org.assertj.core.data.Offset .offset(1e- 10 ))
315
+ assertThat(MatrixMathHelper .degreesToRadians(180.0 ))
316
+ .isCloseTo(Math .PI , org.assertj.core.data.Offset .offset(1e- 10 ))
317
+ assertThat(MatrixMathHelper .degreesToRadians(360.0 ))
318
+ .isCloseTo(2 * Math .PI , org.assertj.core.data.Offset .offset(1e- 10 ))
319
+ }
320
+
321
+ @Test
322
+ fun testApplyPerspective () {
323
+ val matrix = MatrixMathHelper .createIdentityMatrix()
324
+ MatrixMathHelper .applyPerspective(matrix, 100.0 )
325
+
326
+ // Perspective should modify the matrix[11] element
327
+ assertThat(matrix[11 ]).isEqualTo(- 0.01 )
328
+
329
+ // Other elements should remain unchanged for identity matrix
330
+ assertThat(matrix[0 ]).isEqualTo(1.0 )
331
+ assertThat(matrix[5 ]).isEqualTo(1.0 )
332
+ assertThat(matrix[10 ]).isEqualTo(1.0 )
333
+ assertThat(matrix[15 ]).isEqualTo(1.0 )
334
+ }
335
+
336
+ @Test
337
+ fun testApplyScaleX () {
338
+ val matrix = MatrixMathHelper .createIdentityMatrix()
339
+ MatrixMathHelper .applyScaleX(matrix, 2.0 )
340
+
341
+ // ScaleX should modify the matrix[0] element
342
+ assertThat(matrix[0 ]).isEqualTo(2.0 )
343
+
344
+ // Other diagonal elements should remain unchanged
345
+ assertThat(matrix[5 ]).isEqualTo(1.0 )
346
+ assertThat(matrix[10 ]).isEqualTo(1.0 )
347
+ assertThat(matrix[15 ]).isEqualTo(1.0 )
348
+ }
349
+
350
+ @Test
351
+ fun testApplyScaleY () {
352
+ val matrix = MatrixMathHelper .createIdentityMatrix()
353
+ MatrixMathHelper .applyScaleY(matrix, 3.0 )
354
+
355
+ // ScaleY should modify the matrix[5] element
356
+ assertThat(matrix[5 ]).isEqualTo(3.0 )
357
+
358
+ // Other diagonal elements should remain unchanged
359
+ assertThat(matrix[0 ]).isEqualTo(1.0 )
360
+ assertThat(matrix[10 ]).isEqualTo(1.0 )
361
+ assertThat(matrix[15 ]).isEqualTo(1.0 )
362
+ }
363
+
364
+ @Test
365
+ fun testApplyScaleZ () {
366
+ val matrix = MatrixMathHelper .createIdentityMatrix()
367
+ MatrixMathHelper .applyScaleZ(matrix, 4.0 )
368
+
369
+ // ScaleZ should modify the matrix[10] element
370
+ assertThat(matrix[10 ]).isEqualTo(4.0 )
371
+
372
+ // Other diagonal elements should remain unchanged
373
+ assertThat(matrix[0 ]).isEqualTo(1.0 )
374
+ assertThat(matrix[5 ]).isEqualTo(1.0 )
375
+ assertThat(matrix[15 ]).isEqualTo(1.0 )
376
+ }
377
+
378
+ @Test
379
+ fun testApplyTranslate2D () {
380
+ val matrix = MatrixMathHelper .createIdentityMatrix()
381
+ MatrixMathHelper .applyTranslate2D(matrix, 10.0 , 20.0 )
382
+
383
+ // Translation should modify the matrix[12] and matrix[13] elements
384
+ assertThat(matrix[12 ]).isEqualTo(10.0 )
385
+ assertThat(matrix[13 ]).isEqualTo(20.0 )
386
+
387
+ // Other elements should remain unchanged for identity matrix
388
+ assertThat(matrix[0 ]).isEqualTo(1.0 )
389
+ assertThat(matrix[5 ]).isEqualTo(1.0 )
390
+ assertThat(matrix[10 ]).isEqualTo(1.0 )
391
+ assertThat(matrix[15 ]).isEqualTo(1.0 )
392
+ assertThat(matrix[14 ]).isEqualTo(0.0 ) // Z translation should remain 0
393
+ }
394
+
395
+ @Test
396
+ fun testApplyTranslate3D () {
397
+ val matrix = MatrixMathHelper .createIdentityMatrix()
398
+ MatrixMathHelper .applyTranslate3D(matrix, 10.0 , 20.0 , 30.0 )
399
+
400
+ // Translation should modify the matrix[12], matrix[13], and matrix[14] elements
401
+ assertThat(matrix[12 ]).isEqualTo(10.0 )
402
+ assertThat(matrix[13 ]).isEqualTo(20.0 )
403
+ assertThat(matrix[14 ]).isEqualTo(30.0 )
404
+
405
+ // Other elements should remain unchanged for identity matrix
406
+ assertThat(matrix[0 ]).isEqualTo(1.0 )
407
+ assertThat(matrix[5 ]).isEqualTo(1.0 )
408
+ assertThat(matrix[10 ]).isEqualTo(1.0 )
409
+ assertThat(matrix[15 ]).isEqualTo(1.0 )
410
+ }
411
+
412
+ @Test
413
+ fun testApplyRotateX () {
414
+ val matrix = MatrixMathHelper .createIdentityMatrix()
415
+ val angle = Math .PI / 4 // 45 degrees
416
+ MatrixMathHelper .applyRotateX(matrix, angle)
417
+
418
+ // For X rotation, elements [5], [6], [9], [10] should be modified
419
+ assertThat(matrix[5 ]).isCloseTo(cos(angle), org.assertj.core.data.Offset .offset(1e- 10 ))
420
+ assertThat(matrix[6 ]).isCloseTo(sin(angle), org.assertj.core.data.Offset .offset(1e- 10 ))
421
+ assertThat(matrix[9 ]).isCloseTo(- sin(angle), org.assertj.core.data.Offset .offset(1e- 10 ))
422
+ assertThat(matrix[10 ]).isCloseTo(cos(angle), org.assertj.core.data.Offset .offset(1e- 10 ))
423
+
424
+ // Other diagonal elements should remain unchanged
425
+ assertThat(matrix[0 ]).isEqualTo(1.0 )
426
+ assertThat(matrix[15 ]).isEqualTo(1.0 )
427
+ }
428
+
429
+ @Test
430
+ fun testApplyRotateY () {
431
+ val matrix = MatrixMathHelper .createIdentityMatrix()
432
+ val angle = Math .PI / 4 // 45 degrees
433
+ MatrixMathHelper .applyRotateY(matrix, angle)
434
+
435
+ // For Y rotation, elements [0], [2], [8], [10] should be modified
436
+ assertThat(matrix[0 ]).isCloseTo(cos(angle), org.assertj.core.data.Offset .offset(1e- 10 ))
437
+ assertThat(matrix[2 ]).isCloseTo(- sin(angle), org.assertj.core.data.Offset .offset(1e- 10 ))
438
+ assertThat(matrix[8 ]).isCloseTo(sin(angle), org.assertj.core.data.Offset .offset(1e- 10 ))
439
+ assertThat(matrix[10 ]).isCloseTo(cos(angle), org.assertj.core.data.Offset .offset(1e- 10 ))
440
+
441
+ // Other diagonal elements should remain unchanged
442
+ assertThat(matrix[5 ]).isEqualTo(1.0 )
443
+ assertThat(matrix[15 ]).isEqualTo(1.0 )
444
+ }
445
+
446
+ @Test
447
+ fun testApplyRotateZ () {
448
+ val matrix = MatrixMathHelper .createIdentityMatrix()
449
+ val angle = Math .PI / 4 // 45 degrees
450
+ MatrixMathHelper .applyRotateZ(matrix, angle)
451
+
452
+ // For Z rotation, elements [0], [1], [4], [5] should be modified
453
+ assertThat(matrix[0 ]).isCloseTo(cos(angle), org.assertj.core.data.Offset .offset(1e- 10 ))
454
+ assertThat(matrix[1 ]).isCloseTo(sin(angle), org.assertj.core.data.Offset .offset(1e- 10 ))
455
+ assertThat(matrix[4 ]).isCloseTo(- sin(angle), org.assertj.core.data.Offset .offset(1e- 10 ))
456
+ assertThat(matrix[5 ]).isCloseTo(cos(angle), org.assertj.core.data.Offset .offset(1e- 10 ))
457
+
458
+ // Other diagonal elements should remain unchanged
459
+ assertThat(matrix[10 ]).isEqualTo(1.0 )
460
+ assertThat(matrix[15 ]).isEqualTo(1.0 )
461
+ }
462
+
463
+ @Test
464
+ fun testApplySkewX () {
465
+ val matrix = MatrixMathHelper .createIdentityMatrix()
466
+ val angle = Math .PI / 6 // 30 degrees
467
+ MatrixMathHelper .applySkewX(matrix, angle)
468
+
469
+ // For X skew, element [4] should be modified with tan(angle)
470
+ assertThat(matrix[4 ])
471
+ .isCloseTo(kotlin.math.tan(angle), org.assertj.core.data.Offset .offset(1e- 10 ))
472
+
473
+ // Other diagonal elements should remain unchanged
474
+ assertThat(matrix[0 ]).isEqualTo(1.0 )
475
+ assertThat(matrix[5 ]).isEqualTo(1.0 )
476
+ assertThat(matrix[10 ]).isEqualTo(1.0 )
477
+ assertThat(matrix[15 ]).isEqualTo(1.0 )
478
+ }
479
+
480
+ @Test
481
+ fun testApplySkewY () {
482
+ val matrix = MatrixMathHelper .createIdentityMatrix()
483
+ val angle = Math .PI / 6 // 30 degrees
484
+ MatrixMathHelper .applySkewY(matrix, angle)
485
+
486
+ // For Y skew, element [1] should be modified with tan(angle)
487
+ assertThat(matrix[1 ])
488
+ .isCloseTo(kotlin.math.tan(angle), org.assertj.core.data.Offset .offset(1e- 10 ))
489
+
490
+ // Other diagonal elements should remain unchanged
491
+ assertThat(matrix[0 ]).isEqualTo(1.0 )
492
+ assertThat(matrix[5 ]).isEqualTo(1.0 )
493
+ assertThat(matrix[10 ]).isEqualTo(1.0 )
494
+ assertThat(matrix[15 ]).isEqualTo(1.0 )
495
+ }
496
+
110
497
private fun verifyZRotatedMatrix (degrees : Double , rotX : Double , rotY : Double , rotZ : Double ) {
111
498
val ctx = MatrixDecompositionContext ()
112
499
val matrix = createRotateZ(degreesToRadians(degrees))
0 commit comments