Skip to content

Commit dde4d34

Browse files
cortinicofacebook-github-bot
authored andcommitted
Add more tests for MatrixMathHelper (facebook#52885)
Summary: Pull Request resolved: facebook#52885 Since we recently touched the `MatrixMathHelper` class, as we're looking into moving more matrix operations from Kotlin to C++, I'm going to add more tests to make sure that those matrix math function are behaving correctly. Changelog: [Internal] [Changed] - Reviewed By: mdvacca Differential Revision: D79094323 fbshipit-source-id: 34967b42dc92338724dd20fb7f70a68734f6db28
1 parent 6337cbf commit dde4d34

File tree

1 file changed

+387
-0
lines changed

1 file changed

+387
-0
lines changed

packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/MatrixMathHelperTest.kt

Lines changed: 387 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,393 @@ class MatrixMathHelperTest {
107107
verifyRotatedMatrix(10.0, -95.0, 0.0, -170.0, -85.0, -180.0)
108108
}
109109

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+
110497
private fun verifyZRotatedMatrix(degrees: Double, rotX: Double, rotY: Double, rotZ: Double) {
111498
val ctx = MatrixDecompositionContext()
112499
val matrix = createRotateZ(degreesToRadians(degrees))

0 commit comments

Comments
 (0)