2020-06-16 15:36:08 +02:00
|
|
|
/* Apache License, Version 2.0 */
|
|
|
|
|
|
|
|
#include "testing/testing.h"
|
|
|
|
|
|
|
|
#include "BLI_math_matrix.h"
|
|
|
|
|
|
|
|
TEST(math_matrix, interp_m4_m4m4_regular)
|
|
|
|
{
|
|
|
|
/* Test 4x4 matrix interpolation without singularity, i.e. without axis flip. */
|
|
|
|
|
|
|
|
/* Transposed matrix, so that the code here is written in the same way as print_m4() outputs. */
|
|
|
|
/* This matrix represents T=(0.1, 0.2, 0.3), R=(40, 50, 60) degrees, S=(0.7, 0.8, 0.9) */
|
|
|
|
float matrix_a[4][4] = {
|
|
|
|
{0.224976f, -0.333770f, 0.765074f, 0.100000f},
|
|
|
|
{0.389669f, 0.647565f, 0.168130f, 0.200000f},
|
|
|
|
{-0.536231f, 0.330541f, 0.443163f, 0.300000f},
|
|
|
|
{0.000000f, 0.000000f, 0.000000f, 1.000000f},
|
|
|
|
};
|
|
|
|
transpose_m4(matrix_a);
|
|
|
|
|
|
|
|
float matrix_i[4][4];
|
|
|
|
unit_m4(matrix_i);
|
|
|
|
|
|
|
|
float result[4][4];
|
|
|
|
const float epsilon = 1e-6;
|
|
|
|
interp_m4_m4m4(result, matrix_i, matrix_a, 0.0f);
|
|
|
|
EXPECT_M4_NEAR(result, matrix_i, epsilon);
|
|
|
|
|
|
|
|
interp_m4_m4m4(result, matrix_i, matrix_a, 1.0f);
|
|
|
|
EXPECT_M4_NEAR(result, matrix_a, epsilon);
|
|
|
|
|
|
|
|
/* This matrix is based on the current implementation of the code, and isn't guaranteed to be
|
|
|
|
* correct. It's just consistent with the current implementation. */
|
|
|
|
float matrix_halfway[4][4] = {
|
|
|
|
{0.690643f, -0.253244f, 0.484996f, 0.050000f},
|
|
|
|
{0.271924f, 0.852623f, 0.012348f, 0.100000f},
|
|
|
|
{-0.414209f, 0.137484f, 0.816778f, 0.150000f},
|
|
|
|
{0.000000f, 0.000000f, 0.000000f, 1.000000f},
|
|
|
|
};
|
|
|
|
|
|
|
|
transpose_m4(matrix_halfway);
|
|
|
|
interp_m4_m4m4(result, matrix_i, matrix_a, 0.5f);
|
|
|
|
EXPECT_M4_NEAR(result, matrix_halfway, epsilon);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(math_matrix, interp_m3_m3m3_singularity)
|
|
|
|
{
|
2020-08-11 13:19:09 +10:00
|
|
|
/* A singularity means that there is an axis mirror in the rotation component of the matrix.
|
|
|
|
* This is reflected in its negative determinant.
|
2020-06-16 15:36:08 +02:00
|
|
|
*
|
|
|
|
* The interpolation of 4x4 matrices performs linear interpolation on the translation component,
|
|
|
|
* and then uses the 3x3 interpolation function to handle rotation and scale. As a result, this
|
|
|
|
* test for a singularity in the rotation matrix only needs to test the 3x3 case. */
|
|
|
|
|
|
|
|
/* Transposed matrix, so that the code here is written in the same way as print_m4() outputs. */
|
|
|
|
/* This matrix represents R=(4, 5, 6) degrees, S=(-1, 1, 1) */
|
|
|
|
float matrix_a[3][3] = {
|
|
|
|
{-0.990737f, -0.098227f, 0.093759f},
|
|
|
|
{-0.104131f, 0.992735f, -0.060286f},
|
|
|
|
{0.087156f, 0.069491f, 0.993768f},
|
|
|
|
};
|
|
|
|
transpose_m3(matrix_a);
|
|
|
|
EXPECT_NEAR(-1.0f, determinant_m3_array(matrix_a), 1e-6);
|
|
|
|
|
Allow interpolation of matrices with negative scale / axis flips
The matrix interpolation function `interp_m3_m3m3()` decomposes the
matrices into rotation and scale matrices, converts the rotation
matrices to quaternions, SLERPs the quaternions, and converts the result
back to a matrix. Since quaternions cannot represent axis flips, this
results in interpolation problems like described in T77154.
Our interpolation function is based on "Matrix Animation and Polar
Decomposition", by Ken Shoemake & Tom Duff. The paper states that it
produces invalid results when there is an axis flip in the rotation
matrix (or negative determinant, or negative scale, those all indicate
the same thing). Their solution is to multiply the rotation matrix with
`-I`, where `I` is the identity matrix. This is the same as element-wise
multiplication with `-1.0f`. My proposed solution is to not only do that
with the rotation matrix `R`, but also with the scale matrix `S`. This
ensures that the decomposition of `A = R * S` remains valid, while also
making it possible to conver the rotation component to a quaternion.
There is still an issue when interpolating between matrices with
different determinant. As the determinant represents the change in
volume when that matrix is applied to an object, interpolating between a
negative and a positive matrix will have to go through a zero
determinant. In this case the volume collapses to zero. I don't see this
as a big issue, though, as without this patch Blender would also produce
invalid results anyway.
Reviewed By: brecht, sergey
Differential Revision: https://developer.blender.org/D8048
2020-06-18 10:37:52 +02:00
|
|
|
/* This matrix represents R=(0, 0, 0), S=(-1, 0, 0) */
|
|
|
|
float matrix_b[3][3] = {
|
|
|
|
{-1.0f, 0.0f, 0.0f},
|
|
|
|
{0.0f, 1.0f, 0.0f},
|
|
|
|
{0.0f, 0.0f, 1.0f},
|
|
|
|
};
|
|
|
|
transpose_m3(matrix_b);
|
2020-06-16 15:36:08 +02:00
|
|
|
|
|
|
|
float result[3][3];
|
Allow interpolation of matrices with negative scale / axis flips
The matrix interpolation function `interp_m3_m3m3()` decomposes the
matrices into rotation and scale matrices, converts the rotation
matrices to quaternions, SLERPs the quaternions, and converts the result
back to a matrix. Since quaternions cannot represent axis flips, this
results in interpolation problems like described in T77154.
Our interpolation function is based on "Matrix Animation and Polar
Decomposition", by Ken Shoemake & Tom Duff. The paper states that it
produces invalid results when there is an axis flip in the rotation
matrix (or negative determinant, or negative scale, those all indicate
the same thing). Their solution is to multiply the rotation matrix with
`-I`, where `I` is the identity matrix. This is the same as element-wise
multiplication with `-1.0f`. My proposed solution is to not only do that
with the rotation matrix `R`, but also with the scale matrix `S`. This
ensures that the decomposition of `A = R * S` remains valid, while also
making it possible to conver the rotation component to a quaternion.
There is still an issue when interpolating between matrices with
different determinant. As the determinant represents the change in
volume when that matrix is applied to an object, interpolating between a
negative and a positive matrix will have to go through a zero
determinant. In this case the volume collapses to zero. I don't see this
as a big issue, though, as without this patch Blender would also produce
invalid results anyway.
Reviewed By: brecht, sergey
Differential Revision: https://developer.blender.org/D8048
2020-06-18 10:37:52 +02:00
|
|
|
interp_m3_m3m3(result, matrix_a, matrix_b, 0.0f);
|
|
|
|
EXPECT_M3_NEAR(result, matrix_a, 1e-5);
|
|
|
|
|
|
|
|
interp_m3_m3m3(result, matrix_a, matrix_b, 1.0f);
|
|
|
|
EXPECT_M3_NEAR(result, matrix_b, 1e-5);
|
2020-06-16 15:36:08 +02:00
|
|
|
|
Allow interpolation of matrices with negative scale / axis flips
The matrix interpolation function `interp_m3_m3m3()` decomposes the
matrices into rotation and scale matrices, converts the rotation
matrices to quaternions, SLERPs the quaternions, and converts the result
back to a matrix. Since quaternions cannot represent axis flips, this
results in interpolation problems like described in T77154.
Our interpolation function is based on "Matrix Animation and Polar
Decomposition", by Ken Shoemake & Tom Duff. The paper states that it
produces invalid results when there is an axis flip in the rotation
matrix (or negative determinant, or negative scale, those all indicate
the same thing). Their solution is to multiply the rotation matrix with
`-I`, where `I` is the identity matrix. This is the same as element-wise
multiplication with `-1.0f`. My proposed solution is to not only do that
with the rotation matrix `R`, but also with the scale matrix `S`. This
ensures that the decomposition of `A = R * S` remains valid, while also
making it possible to conver the rotation component to a quaternion.
There is still an issue when interpolating between matrices with
different determinant. As the determinant represents the change in
volume when that matrix is applied to an object, interpolating between a
negative and a positive matrix will have to go through a zero
determinant. In this case the volume collapses to zero. I don't see this
as a big issue, though, as without this patch Blender would also produce
invalid results anyway.
Reviewed By: brecht, sergey
Differential Revision: https://developer.blender.org/D8048
2020-06-18 10:37:52 +02:00
|
|
|
interp_m3_m3m3(result, matrix_a, matrix_b, 0.5f);
|
|
|
|
float expect[3][3] = {
|
|
|
|
{-0.997681f, -0.049995f, 0.046186f},
|
|
|
|
{-0.051473f, 0.998181f, -0.031385f},
|
|
|
|
{0.044533f, 0.033689f, 0.998440f},
|
|
|
|
};
|
|
|
|
transpose_m3(expect);
|
|
|
|
EXPECT_M3_NEAR(result, expect, 1e-5);
|
|
|
|
|
|
|
|
/* Interpolating between a matrix with and without axis flip can cause it to go through a zero
|
|
|
|
* point. The determinant det(A) of a matrix represents the change in volume; interpolating
|
|
|
|
* between matrices with det(A)=-1 and det(B)=1 will have to go through a point where
|
|
|
|
* det(result)=0, so where the volume becomes zero. */
|
|
|
|
float matrix_i[3][3];
|
|
|
|
unit_m3(matrix_i);
|
|
|
|
zero_m3(expect);
|
|
|
|
interp_m3_m3m3(result, matrix_a, matrix_i, 0.5f);
|
|
|
|
EXPECT_NEAR(0.0f, determinant_m3_array(result), 1e-5);
|
|
|
|
EXPECT_M3_NEAR(result, expect, 1e-5);
|
2020-06-16 15:36:08 +02:00
|
|
|
}
|