Blender  V3.3
BLI_math_rotation_test.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: Apache-2.0 */
2 
3 #include "testing/testing.h"
4 
5 #include "BLI_math_base.h"
6 #include "BLI_math_rotation.h"
7 #include "BLI_math_rotation.hh"
8 #include "BLI_math_vector.hh"
9 
10 #include "BLI_vector.hh"
11 
12 #include <cmath>
13 
14 /* Test that quaternion converts to itself via matrix. */
15 static void test_quat_to_mat_to_quat(float w, float x, float y, float z)
16 {
17  float in_quat[4] = {w, x, y, z};
18  float norm_quat[4], matrix[3][3], out_quat[4];
19 
20  normalize_qt_qt(norm_quat, in_quat);
21  quat_to_mat3(matrix, norm_quat);
22  mat3_normalized_to_quat(out_quat, matrix);
23 
24  /* The expected result is flipped (each orientation corresponds to 2 quats) */
25  if (w < 0) {
26  mul_qt_fl(norm_quat, -1);
27  }
28 
29  EXPECT_V4_NEAR(norm_quat, out_quat, FLT_EPSILON);
30 }
31 
32 TEST(math_rotation, quat_to_mat_to_quat_rot180)
33 {
34  test_quat_to_mat_to_quat(1, 0, 0, 0);
35  test_quat_to_mat_to_quat(0, 1, 0, 0);
36  test_quat_to_mat_to_quat(0, 0, 1, 0);
37  test_quat_to_mat_to_quat(0, 0, 0, 1);
38 }
39 
40 TEST(math_rotation, quat_to_mat_to_quat_rot180n)
41 {
42  test_quat_to_mat_to_quat(-1.000f, 0, 0, 0);
43  test_quat_to_mat_to_quat(-1e-20f, -1, 0, 0);
44  test_quat_to_mat_to_quat(-1e-20f, 0, -1, 0);
45  test_quat_to_mat_to_quat(-1e-20f, 0, 0, -1);
46 }
47 
48 TEST(math_rotation, quat_to_mat_to_quat_rot90)
49 {
50  const float s2 = 1 / sqrtf(2);
51  test_quat_to_mat_to_quat(s2, s2, 0, 0);
52  test_quat_to_mat_to_quat(s2, -s2, 0, 0);
53  test_quat_to_mat_to_quat(s2, 0, s2, 0);
54  test_quat_to_mat_to_quat(s2, 0, -s2, 0);
55  test_quat_to_mat_to_quat(s2, 0, 0, s2);
56  test_quat_to_mat_to_quat(s2, 0, 0, -s2);
57 }
58 
59 TEST(math_rotation, quat_to_mat_to_quat_rot90n)
60 {
61  const float s2 = 1 / sqrtf(2);
62  test_quat_to_mat_to_quat(-s2, s2, 0, 0);
63  test_quat_to_mat_to_quat(-s2, -s2, 0, 0);
64  test_quat_to_mat_to_quat(-s2, 0, s2, 0);
65  test_quat_to_mat_to_quat(-s2, 0, -s2, 0);
66  test_quat_to_mat_to_quat(-s2, 0, 0, s2);
67  test_quat_to_mat_to_quat(-s2, 0, 0, -s2);
68 }
69 
70 TEST(math_rotation, quat_to_mat_to_quat_bad_T83196)
71 {
72  test_quat_to_mat_to_quat(0.0032f, 0.9999f, -0.0072f, -0.0100f);
73  test_quat_to_mat_to_quat(0.0058f, 0.9999f, -0.0090f, -0.0101f);
74  test_quat_to_mat_to_quat(0.0110f, 0.9998f, -0.0140f, -0.0104f);
75  test_quat_to_mat_to_quat(0.0142f, 0.9997f, -0.0192f, -0.0107f);
76  test_quat_to_mat_to_quat(0.0149f, 0.9996f, -0.0212f, -0.0107f);
77 }
78 
79 TEST(math_rotation, quat_to_mat_to_quat_bad_negative)
80 {
81  /* This shouldn't produce a negative q[0]. */
82  test_quat_to_mat_to_quat(0.5f - 1e-6f, 0, -sqrtf(3) / 2 - 1e-6f, 0);
83 }
84 
85 TEST(math_rotation, quat_to_mat_to_quat_near_1000)
86 {
87  test_quat_to_mat_to_quat(0.9999f, 0.01f, -0.001f, -0.01f);
88  test_quat_to_mat_to_quat(0.9999f, 0.02f, -0.002f, -0.02f);
89  test_quat_to_mat_to_quat(0.9999f, 0.03f, -0.003f, -0.03f);
90  test_quat_to_mat_to_quat(0.9999f, 0.04f, -0.004f, -0.04f);
91  test_quat_to_mat_to_quat(0.9999f, 0.05f, -0.005f, -0.05f);
92  test_quat_to_mat_to_quat(0.999f, 0.10f, -0.010f, -0.10f);
93  test_quat_to_mat_to_quat(0.99f, 0.15f, -0.015f, -0.15f);
94  test_quat_to_mat_to_quat(0.98f, 0.20f, -0.020f, -0.20f);
95  test_quat_to_mat_to_quat(0.97f, 0.25f, -0.025f, -0.25f);
96  test_quat_to_mat_to_quat(0.95f, 0.30f, -0.030f, -0.30f);
97 }
98 
99 TEST(math_rotation, quat_to_mat_to_quat_near_0100)
100 {
101  test_quat_to_mat_to_quat(0.01f, 0.9999f, -0.001f, -0.01f);
102  test_quat_to_mat_to_quat(0.02f, 0.9999f, -0.002f, -0.02f);
103  test_quat_to_mat_to_quat(0.03f, 0.9999f, -0.003f, -0.03f);
104  test_quat_to_mat_to_quat(0.04f, 0.9999f, -0.004f, -0.04f);
105  test_quat_to_mat_to_quat(0.05f, 0.9999f, -0.005f, -0.05f);
106  test_quat_to_mat_to_quat(0.10f, 0.999f, -0.010f, -0.10f);
107  test_quat_to_mat_to_quat(0.15f, 0.99f, -0.015f, -0.15f);
108  test_quat_to_mat_to_quat(0.20f, 0.98f, -0.020f, -0.20f);
109  test_quat_to_mat_to_quat(0.25f, 0.97f, -0.025f, -0.25f);
110  test_quat_to_mat_to_quat(0.30f, 0.95f, -0.030f, -0.30f);
111 }
112 
113 TEST(math_rotation, quat_to_mat_to_quat_near_0010)
114 {
115  test_quat_to_mat_to_quat(0.01f, -0.001f, 0.9999f, -0.01f);
116  test_quat_to_mat_to_quat(0.02f, -0.002f, 0.9999f, -0.02f);
117  test_quat_to_mat_to_quat(0.03f, -0.003f, 0.9999f, -0.03f);
118  test_quat_to_mat_to_quat(0.04f, -0.004f, 0.9999f, -0.04f);
119  test_quat_to_mat_to_quat(0.05f, -0.005f, 0.9999f, -0.05f);
120  test_quat_to_mat_to_quat(0.10f, -0.010f, 0.999f, -0.10f);
121  test_quat_to_mat_to_quat(0.15f, -0.015f, 0.99f, -0.15f);
122  test_quat_to_mat_to_quat(0.20f, -0.020f, 0.98f, -0.20f);
123  test_quat_to_mat_to_quat(0.25f, -0.025f, 0.97f, -0.25f);
124  test_quat_to_mat_to_quat(0.30f, -0.030f, 0.95f, -0.30f);
125 }
126 
127 TEST(math_rotation, quat_to_mat_to_quat_near_0001)
128 {
129  test_quat_to_mat_to_quat(0.01f, -0.001f, -0.01f, 0.9999f);
130  test_quat_to_mat_to_quat(0.02f, -0.002f, -0.02f, 0.9999f);
131  test_quat_to_mat_to_quat(0.03f, -0.003f, -0.03f, 0.9999f);
132  test_quat_to_mat_to_quat(0.04f, -0.004f, -0.04f, 0.9999f);
133  test_quat_to_mat_to_quat(0.05f, -0.005f, -0.05f, 0.9999f);
134  test_quat_to_mat_to_quat(0.10f, -0.010f, -0.10f, 0.999f);
135  test_quat_to_mat_to_quat(0.15f, -0.015f, -0.15f, 0.99f);
136  test_quat_to_mat_to_quat(0.20f, -0.020f, -0.20f, 0.98f);
137  test_quat_to_mat_to_quat(0.25f, -0.025f, -0.25f, 0.97f);
138  test_quat_to_mat_to_quat(0.30f, -0.030f, -0.30f, 0.95f);
139 }
140 
141 TEST(math_rotation, quat_split_swing_and_twist_negative)
142 {
143  const float input[4] = {-0.5f, 0, sqrtf(3) / 2, 0};
144  const float expected_swing[4] = {1.0f, 0, 0, 0};
145  const float expected_twist[4] = {0.5f, 0, -sqrtf(3) / 2, 0};
146  float swing[4], twist[4];
147 
148  float twist_angle = quat_split_swing_and_twist(input, 1, swing, twist);
149 
150  EXPECT_NEAR(twist_angle, -M_PI * 2 / 3, FLT_EPSILON);
151  EXPECT_V4_NEAR(swing, expected_swing, FLT_EPSILON);
152  EXPECT_V4_NEAR(twist, expected_twist, FLT_EPSILON);
153 }
154 
155 /* -------------------------------------------------------------------- */
159 static void test_sin_cos_from_fraction_accuracy(const int range, const float expected_eps)
160 {
161  for (int i = 0; i < range; i++) {
162  float sin_cos_fl[2];
163  sin_cos_from_fraction(i, range, &sin_cos_fl[0], &sin_cos_fl[1]);
164  const float phi = (float)(2.0 * M_PI) * ((float)i / (float)range);
165  const float sin_cos_test_fl[2] = {sinf(phi), cosf(phi)};
166  EXPECT_V2_NEAR(sin_cos_fl, sin_cos_test_fl, expected_eps);
167  }
168 }
169 
171 TEST(math_rotation, sin_cos_from_fraction_accuracy)
172 {
173  for (int range = 1; range <= 64; range++) {
175  }
176 }
177 
179 static void test_sin_cos_from_fraction_symmetry(const int range)
180 {
181  /* The expected number of unique numbers depends on the range being a multiple of 4/2/1. */
182  const enum {
183  MULTIPLE_OF_1 = 1,
184  MULTIPLE_OF_2 = 2,
185  MULTIPLE_OF_4 = 3,
186  } multiple_of = (range & 1) ? MULTIPLE_OF_1 : ((range & 3) ? MULTIPLE_OF_2 : MULTIPLE_OF_4);
187 
189  coords.reserve(range);
190  for (int i = 0; i < range; i++) {
191  float sin_cos_fl[2];
192  sin_cos_from_fraction(i, range, &sin_cos_fl[0], &sin_cos_fl[1]);
193  switch (multiple_of) {
194  case MULTIPLE_OF_1: {
195  sin_cos_fl[0] = fabsf(sin_cos_fl[0]);
196  break;
197  }
198  case MULTIPLE_OF_2: {
199  sin_cos_fl[0] = fabsf(sin_cos_fl[0]);
200  sin_cos_fl[1] = fabsf(sin_cos_fl[1]);
201  break;
202  }
203  case MULTIPLE_OF_4: {
204  sin_cos_fl[0] = fabsf(sin_cos_fl[0]);
205  sin_cos_fl[1] = fabsf(sin_cos_fl[1]);
206  if (sin_cos_fl[0] > sin_cos_fl[1]) {
207  SWAP(float, sin_cos_fl[0], sin_cos_fl[1]);
208  }
209  break;
210  }
211  }
212  coords.append_unchecked(sin_cos_fl);
213  }
214  /* Sort, then count unique items. */
215  std::sort(coords.begin(), coords.end(), [](const blender::float2 &a, const blender::float2 &b) {
216  float delta = b[0] - a[0];
217  if (delta == 0.0f) {
218  delta = b[1] - a[1];
219  }
220  return delta > 0.0f;
221  });
222  int unique_coords_count = 1;
223  if (range > 1) {
224  int i_prev = 0;
225  for (int i = 1; i < range; i_prev = i++) {
226  if (coords[i_prev] != coords[i]) {
227  unique_coords_count += 1;
228  }
229  }
230  }
231  switch (multiple_of) {
232  case MULTIPLE_OF_1: {
233  EXPECT_EQ(unique_coords_count, (range / 2) + 1);
234  break;
235  }
236  case MULTIPLE_OF_2: {
237  EXPECT_EQ(unique_coords_count, (range / 4) + 1);
238  break;
239  }
240  case MULTIPLE_OF_4: {
241  EXPECT_EQ(unique_coords_count, (range / 8) + 1);
242  break;
243  }
244  }
245 }
246 
247 TEST(math_rotation, sin_cos_from_fraction_symmetry)
248 {
249  for (int range = 1; range <= 64; range++) {
251  }
252 }
253 
257 
258 TEST(math_rotation, RotateDirectionAroundAxis)
259 {
260  const float3 a = rotate_direction_around_axis({1, 0, 0}, {0, 0, 1}, M_PI_2);
261  EXPECT_NEAR(a.x, 0.0f, FLT_EPSILON);
262  EXPECT_NEAR(a.y, 1.0f, FLT_EPSILON);
263  EXPECT_NEAR(a.z, 0.0f, FLT_EPSILON);
264  const float3 b = rotate_direction_around_axis({1, 0, 0}, {0, 0, 1}, M_PI);
265  EXPECT_NEAR(b.x, -1.0f, FLT_EPSILON);
266  EXPECT_NEAR(b.y, 0.0f, FLT_EPSILON);
267  EXPECT_NEAR(b.z, 0.0f, FLT_EPSILON);
268  const float3 c = rotate_direction_around_axis({0, 0, 1}, {0, 0, 1}, 0.0f);
269  EXPECT_NEAR(c.x, 0.0f, FLT_EPSILON);
270  EXPECT_NEAR(c.y, 0.0f, FLT_EPSILON);
271  EXPECT_NEAR(c.z, 1.0f, FLT_EPSILON);
272 }
273 
274 } // namespace blender::math::tests
typedef float(TangentPoint)[2]
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
#define M_PI_2
Definition: BLI_math_base.h:23
#define M_PI
Definition: BLI_math_base.h:20
void sin_cos_from_fraction(int numerator, int denominator, float *r_sin, float *r_cos)
void mul_qt_fl(float q[4], float f)
float normalize_qt_qt(float r[4], const float q[4])
void quat_to_mat3(float mat[3][3], const float q[4])
float quat_split_swing_and_twist(const float q[4], int axis, float r_swing[4], float r_twist[4])
void mat3_normalized_to_quat(float q[4], const float mat[3][3])
TEST(math_rotation, quat_to_mat_to_quat_rot180)
static void test_sin_cos_from_fraction_accuracy(const int range, const float expected_eps)
static void test_quat_to_mat_to_quat(float w, float x, float y, float z)
static void test_sin_cos_from_fraction_symmetry(const int range)
#define SWAP(type, a, b)
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble z
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
void sort(btMatrix3x3 &U, btVector3 &sigma, btMatrix3x3 &V, int t)
Helper function of 3X3 SVD for sorting singular values.
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition: btQuadWord.h:119
void append_unchecked(const T &value)
Definition: BLI_vector.hh:484
void reserve(const int64_t min_capacity)
Definition: BLI_vector.hh:340
#define sinf(x)
Definition: cuda/compat.h:102
#define cosf(x)
Definition: cuda/compat.h:101
ccl_global KernelShaderEvalInput * input
#define fabsf(x)
Definition: metal/compat.h:219
#define sqrtf(x)
Definition: metal/compat.h:243
static unsigned c
Definition: RandGen.cpp:83
static unsigned a[3]
Definition: RandGen.cpp:78
TEST(math_rotation, RotateDirectionAroundAxis)
float3 rotate_direction_around_axis(const float3 &direction, const float3 &axis, float angle)
static const pxr::TfToken b("b", pxr::TfToken::Immortal)