Blender V5.0
BLI_math_rotation_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include "testing/testing.h"
6
7#include "BLI_math_base.h"
8#include "BLI_math_matrix.h"
9#include "BLI_math_rotation.h"
10#include "BLI_math_rotation.hh"
12#include "BLI_math_vector.hh"
13
14#include "BLI_vector.hh"
15
16#include <cmath>
17
18/* Test that quaternion converts to itself via matrix. */
19static void test_quat_to_mat_to_quat(float w, float x, float y, float z)
20{
21 float in_quat[4] = {w, x, y, z};
22 float norm_quat[4], matrix[3][3], out_quat[4];
23
24 normalize_qt_qt(norm_quat, in_quat);
25 quat_to_mat3(matrix, norm_quat);
26 mat3_normalized_to_quat(out_quat, matrix);
27
28 /* The expected result is flipped (each orientation corresponds to 2 quaternions). */
29 if (w < 0) {
30 mul_qt_fl(norm_quat, -1);
31 }
32
33 EXPECT_V4_NEAR(norm_quat, out_quat, FLT_EPSILON);
34}
35
36TEST(math_rotation, quat_to_mat_to_quat_rot180)
37{
38 test_quat_to_mat_to_quat(1, 0, 0, 0);
39 test_quat_to_mat_to_quat(0, 1, 0, 0);
40 test_quat_to_mat_to_quat(0, 0, 1, 0);
41 test_quat_to_mat_to_quat(0, 0, 0, 1);
42}
43
44TEST(math_rotation, quat_to_mat_to_quat_rot180n)
45{
46 test_quat_to_mat_to_quat(-1.000f, 0, 0, 0);
47 test_quat_to_mat_to_quat(-1e-20f, -1, 0, 0);
48 test_quat_to_mat_to_quat(-1e-20f, 0, -1, 0);
49 test_quat_to_mat_to_quat(-1e-20f, 0, 0, -1);
50}
51
52TEST(math_rotation, quat_to_mat_to_quat_rot90)
53{
54 const float s2 = 1 / sqrtf(2);
55 test_quat_to_mat_to_quat(s2, s2, 0, 0);
56 test_quat_to_mat_to_quat(s2, -s2, 0, 0);
57 test_quat_to_mat_to_quat(s2, 0, s2, 0);
58 test_quat_to_mat_to_quat(s2, 0, -s2, 0);
59 test_quat_to_mat_to_quat(s2, 0, 0, s2);
60 test_quat_to_mat_to_quat(s2, 0, 0, -s2);
61}
62
63TEST(math_rotation, quat_to_mat_to_quat_rot90n)
64{
65 const float s2 = 1 / sqrtf(2);
66 test_quat_to_mat_to_quat(-s2, s2, 0, 0);
67 test_quat_to_mat_to_quat(-s2, -s2, 0, 0);
68 test_quat_to_mat_to_quat(-s2, 0, s2, 0);
69 test_quat_to_mat_to_quat(-s2, 0, -s2, 0);
70 test_quat_to_mat_to_quat(-s2, 0, 0, s2);
71 test_quat_to_mat_to_quat(-s2, 0, 0, -s2);
72}
73
74TEST(math_rotation, quat_to_mat_to_quat_bad_T83196)
75{
76 test_quat_to_mat_to_quat(0.0032f, 0.9999f, -0.0072f, -0.0100f);
77 test_quat_to_mat_to_quat(0.0058f, 0.9999f, -0.0090f, -0.0101f);
78 test_quat_to_mat_to_quat(0.0110f, 0.9998f, -0.0140f, -0.0104f);
79 test_quat_to_mat_to_quat(0.0142f, 0.9997f, -0.0192f, -0.0107f);
80 test_quat_to_mat_to_quat(0.0149f, 0.9996f, -0.0212f, -0.0107f);
81}
82
83TEST(math_rotation, quat_to_mat_to_quat_bad_negative)
84{
85 /* This shouldn't produce a negative q[0]. */
86 test_quat_to_mat_to_quat(0.5f - 1e-6f, 0, -sqrtf(3) / 2 - 1e-6f, 0);
87}
88
89TEST(math_rotation, quat_to_mat_to_quat_near_1000)
90{
91 test_quat_to_mat_to_quat(0.9999f, 0.01f, -0.001f, -0.01f);
92 test_quat_to_mat_to_quat(0.9999f, 0.02f, -0.002f, -0.02f);
93 test_quat_to_mat_to_quat(0.9999f, 0.03f, -0.003f, -0.03f);
94 test_quat_to_mat_to_quat(0.9999f, 0.04f, -0.004f, -0.04f);
95 test_quat_to_mat_to_quat(0.9999f, 0.05f, -0.005f, -0.05f);
96 test_quat_to_mat_to_quat(0.999f, 0.10f, -0.010f, -0.10f);
97 test_quat_to_mat_to_quat(0.99f, 0.15f, -0.015f, -0.15f);
98 test_quat_to_mat_to_quat(0.98f, 0.20f, -0.020f, -0.20f);
99 test_quat_to_mat_to_quat(0.97f, 0.25f, -0.025f, -0.25f);
100 test_quat_to_mat_to_quat(0.95f, 0.30f, -0.030f, -0.30f);
101}
102
103TEST(math_rotation, quat_to_mat_to_quat_near_0100)
104{
105 test_quat_to_mat_to_quat(0.01f, 0.9999f, -0.001f, -0.01f);
106 test_quat_to_mat_to_quat(0.02f, 0.9999f, -0.002f, -0.02f);
107 test_quat_to_mat_to_quat(0.03f, 0.9999f, -0.003f, -0.03f);
108 test_quat_to_mat_to_quat(0.04f, 0.9999f, -0.004f, -0.04f);
109 test_quat_to_mat_to_quat(0.05f, 0.9999f, -0.005f, -0.05f);
110 test_quat_to_mat_to_quat(0.10f, 0.999f, -0.010f, -0.10f);
111 test_quat_to_mat_to_quat(0.15f, 0.99f, -0.015f, -0.15f);
112 test_quat_to_mat_to_quat(0.20f, 0.98f, -0.020f, -0.20f);
113 test_quat_to_mat_to_quat(0.25f, 0.97f, -0.025f, -0.25f);
114 test_quat_to_mat_to_quat(0.30f, 0.95f, -0.030f, -0.30f);
115}
116
117TEST(math_rotation, quat_to_mat_to_quat_near_0010)
118{
119 test_quat_to_mat_to_quat(0.01f, -0.001f, 0.9999f, -0.01f);
120 test_quat_to_mat_to_quat(0.02f, -0.002f, 0.9999f, -0.02f);
121 test_quat_to_mat_to_quat(0.03f, -0.003f, 0.9999f, -0.03f);
122 test_quat_to_mat_to_quat(0.04f, -0.004f, 0.9999f, -0.04f);
123 test_quat_to_mat_to_quat(0.05f, -0.005f, 0.9999f, -0.05f);
124 test_quat_to_mat_to_quat(0.10f, -0.010f, 0.999f, -0.10f);
125 test_quat_to_mat_to_quat(0.15f, -0.015f, 0.99f, -0.15f);
126 test_quat_to_mat_to_quat(0.20f, -0.020f, 0.98f, -0.20f);
127 test_quat_to_mat_to_quat(0.25f, -0.025f, 0.97f, -0.25f);
128 test_quat_to_mat_to_quat(0.30f, -0.030f, 0.95f, -0.30f);
129}
130
131TEST(math_rotation, quat_to_mat_to_quat_near_0001)
132{
133 test_quat_to_mat_to_quat(0.01f, -0.001f, -0.01f, 0.9999f);
134 test_quat_to_mat_to_quat(0.02f, -0.002f, -0.02f, 0.9999f);
135 test_quat_to_mat_to_quat(0.03f, -0.003f, -0.03f, 0.9999f);
136 test_quat_to_mat_to_quat(0.04f, -0.004f, -0.04f, 0.9999f);
137 test_quat_to_mat_to_quat(0.05f, -0.005f, -0.05f, 0.9999f);
138 test_quat_to_mat_to_quat(0.10f, -0.010f, -0.10f, 0.999f);
139 test_quat_to_mat_to_quat(0.15f, -0.015f, -0.15f, 0.99f);
140 test_quat_to_mat_to_quat(0.20f, -0.020f, -0.20f, 0.98f);
141 test_quat_to_mat_to_quat(0.25f, -0.025f, -0.25f, 0.97f);
142 test_quat_to_mat_to_quat(0.30f, -0.030f, -0.30f, 0.95f);
143}
144
145/* A zeroed matrix converted to a quaternion and back should not add rotation, see: #101848 */
146TEST(math_rotation, quat_to_mat_to_quat_zeroed_matrix)
147{
148 float matrix_zeroed[3][3] = {{0.0f}};
149 float matrix_result[3][3];
150 float matrix_unit[3][3];
151 float out_quat[4];
152
153 unit_m3(matrix_unit);
154 mat3_normalized_to_quat(out_quat, matrix_zeroed);
155 quat_to_mat3(matrix_result, out_quat);
156
157 EXPECT_M3_NEAR(matrix_unit, matrix_result, FLT_EPSILON);
158}
159
160TEST(math_rotation, quat_split_swing_and_twist_negative)
161{
162 const float input[4] = {-0.5f, 0, sqrtf(3) / 2, 0};
163 const float expected_swing[4] = {1.0f, 0, 0, 0};
164 const float expected_twist[4] = {0.5f, 0, -sqrtf(3) / 2, 0};
165 float swing[4], twist[4];
166
167 float twist_angle = quat_split_swing_and_twist(input, 1, swing, twist);
168
169 EXPECT_NEAR(twist_angle, -M_PI * 2 / 3, FLT_EPSILON);
170 EXPECT_V4_NEAR(swing, expected_swing, FLT_EPSILON);
171 EXPECT_V4_NEAR(twist, expected_twist, FLT_EPSILON);
172}
173
174TEST(math_rotation, mat3_normalized_to_quat_fast_degenerate)
175{
176 /* This input will cause floating point issues, which would produce a non-unit
177 * quaternion if the call to `normalize_qt` were to be removed. This
178 * particular matrix was taken from a production file of Pet Projects that
179 * caused problems. */
180 const float input[3][3] = {
181 {1.0000000000, -0.0000006315, -0.0000000027},
182 {0.0000009365, 1.0000000000, -0.0000000307},
183 {0.0000001964, 0.2103530765, 0.9776254892},
184 };
185 const float expect_quat[4] = {
186 0.99860459566116333,
187 -0.052810292690992355,
188 4.9985139582986449e-08,
189 -3.93654971730939e-07,
190 };
191 ASSERT_FLOAT_EQ(1.0f, dot_qtqt(expect_quat, expect_quat))
192 << "expected quaternion should be normal";
193
194 float actual_quat[4];
196 EXPECT_FLOAT_EQ(1.0f, dot_qtqt(actual_quat, actual_quat));
197 EXPECT_V4_NEAR(expect_quat, actual_quat, FLT_EPSILON);
198}
199
200/* -------------------------------------------------------------------- */
203
204static void test_sin_cos_from_fraction_accuracy(const int range, const float expected_eps)
205{
206 for (int i = 0; i < range; i++) {
207 float sin_cos_fl[2];
208 sin_cos_from_fraction(i, range, &sin_cos_fl[0], &sin_cos_fl[1]);
209 const float phi = float(2.0 * M_PI) * (float(i) / float(range));
210 const float sin_cos_test_fl[2] = {sinf(phi), cosf(phi)};
211 EXPECT_V2_NEAR(sin_cos_fl, sin_cos_test_fl, expected_eps);
212 }
213}
214
216TEST(math_rotation, sin_cos_from_fraction_accuracy)
217{
218 for (int range = 1; range <= 64; range++) {
220 }
221}
222
224static void test_sin_cos_from_fraction_symmetry(const int range)
225{
226 /* The expected number of unique numbers depends on the range being a multiple of 4/2/1. */
227 const enum {
228 MULTIPLE_OF_1 = 1,
229 MULTIPLE_OF_2 = 2,
230 MULTIPLE_OF_4 = 3,
231 } multiple_of = (range & 1) ? MULTIPLE_OF_1 : ((range & 3) ? MULTIPLE_OF_2 : MULTIPLE_OF_4);
232
234 coords.reserve(range);
235 for (int i = 0; i < range; i++) {
236 float sin_cos_fl[2];
237 sin_cos_from_fraction(i, range, &sin_cos_fl[0], &sin_cos_fl[1]);
238 switch (multiple_of) {
239 case MULTIPLE_OF_1: {
240 sin_cos_fl[0] = fabsf(sin_cos_fl[0]);
241 break;
242 }
243 case MULTIPLE_OF_2: {
244 sin_cos_fl[0] = fabsf(sin_cos_fl[0]);
245 sin_cos_fl[1] = fabsf(sin_cos_fl[1]);
246 break;
247 }
248 case MULTIPLE_OF_4: {
249 sin_cos_fl[0] = fabsf(sin_cos_fl[0]);
250 sin_cos_fl[1] = fabsf(sin_cos_fl[1]);
251 if (sin_cos_fl[0] > sin_cos_fl[1]) {
252 std::swap(sin_cos_fl[0], sin_cos_fl[1]);
253 }
254 break;
255 }
256 }
257 coords.append_unchecked(sin_cos_fl);
258 }
259 /* Sort, then count unique items. */
260 std::sort(coords.begin(), coords.end(), [](const blender::float2 &a, const blender::float2 &b) {
261 float delta = b[0] - a[0];
262 if (delta == 0.0f) {
263 delta = b[1] - a[1];
264 }
265 return delta > 0.0f;
266 });
267 int unique_coords_count = 1;
268 if (range > 1) {
269 int i_prev = 0;
270 for (int i = 1; i < range; i_prev = i++) {
271 if (coords[i_prev] != coords[i]) {
272 unique_coords_count += 1;
273 }
274 }
275 }
276 switch (multiple_of) {
277 case MULTIPLE_OF_1: {
278 EXPECT_EQ(unique_coords_count, (range / 2) + 1);
279 break;
280 }
281 case MULTIPLE_OF_2: {
282 EXPECT_EQ(unique_coords_count, (range / 4) + 1);
283 break;
284 }
285 case MULTIPLE_OF_4: {
286 EXPECT_EQ(unique_coords_count, (range / 8) + 1);
287 break;
288 }
289 }
290}
291
292TEST(math_rotation, sin_cos_from_fraction_symmetry)
293{
294 for (int range = 1; range <= 64; range++) {
296 }
297}
298
300
302
303TEST(math_rotation, DefaultConstructor)
304{
305 Quaternion quat{};
306 EXPECT_EQ(quat.x, 0.0f);
307 EXPECT_EQ(quat.y, 0.0f);
308 EXPECT_EQ(quat.z, 0.0f);
309 EXPECT_EQ(quat.w, 0.0f);
310
311 EulerXYZ eul{};
312 EXPECT_EQ(eul.x(), 0.0f);
313 EXPECT_EQ(eul.y(), 0.0f);
314 EXPECT_EQ(eul.z(), 0.0f);
315}
316
317TEST(math_rotation, RotateDirectionAroundAxis)
318{
319 const float3 a = rotate_direction_around_axis({1, 0, 0}, {0, 0, 1}, M_PI_2);
320 EXPECT_NEAR(a.x, 0.0f, FLT_EPSILON);
321 EXPECT_NEAR(a.y, 1.0f, FLT_EPSILON);
322 EXPECT_NEAR(a.z, 0.0f, FLT_EPSILON);
323 const float3 b = rotate_direction_around_axis({1, 0, 0}, {0, 0, 1}, M_PI);
324 EXPECT_NEAR(b.x, -1.0f, FLT_EPSILON);
325 EXPECT_NEAR(b.y, 0.0f, FLT_EPSILON);
326 EXPECT_NEAR(b.z, 0.0f, FLT_EPSILON);
327 const float3 c = rotate_direction_around_axis({0, 0, 1}, {0, 0, 1}, 0.0f);
328 EXPECT_NEAR(c.x, 0.0f, FLT_EPSILON);
329 EXPECT_NEAR(c.y, 0.0f, FLT_EPSILON);
330 EXPECT_NEAR(c.z, 1.0f, FLT_EPSILON);
331}
332
333TEST(math_rotation, AxisAngleConstructors)
334{
335 AxisAngle a({0.0f, 0.0f, 1.0f}, M_PI_2);
336 EXPECT_V3_NEAR(a.axis(), float3(0, 0, 1), 1e-4);
337 EXPECT_NEAR(float(a.angle()), M_PI_2, 1e-4);
338 EXPECT_NEAR(sin(a.angle()), 1.0f, 1e-4);
339 EXPECT_NEAR(cos(a.angle()), 0.0f, 1e-4);
340
342 EXPECT_V3_NEAR(b.axis(), float3(0, 0, 1), 1e-4);
343 EXPECT_NEAR(float(b.angle()), M_PI_2, 1e-4);
344 EXPECT_NEAR(b.angle().sin(), 1.0f, 1e-4);
345 EXPECT_NEAR(b.angle().cos(), 0.0f, 1e-4);
346
347 AxisAngle axis_angle_basis = AxisAngle(AxisSigned::Y_NEG, M_PI);
348 EXPECT_EQ(axis_angle_basis.axis(), float3(0.0f, -1.0f, 0.0f));
349 EXPECT_EQ(axis_angle_basis.angle(), M_PI);
350
351 AxisAngle c({1.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f});
352 EXPECT_V3_NEAR(c.axis(), float3(0, 0, 1), 1e-4);
353 EXPECT_NEAR(float(c.angle()), M_PI_2, 1e-4);
354
355 AxisAngle d({1.0f, 0.0f, 0.0f}, {0.0f, -1.0f, 0.0f});
356 EXPECT_V3_NEAR(d.axis(), float3(0, 0, -1), 1e-4);
357 EXPECT_NEAR(float(d.angle()), M_PI_2, 1e-4);
358}
359
360TEST(math_rotation, QuaternionDot)
361{
362 Quaternion q1(1.0f, 2.0f, 3.0f, 4.0f);
363 Quaternion q2(2.0f, -3.0f, 5.0f, 100.0f);
364 EXPECT_EQ(math::dot(q1, q2), 411.0f);
365}
366
367TEST(math_rotation, QuaternionConjugate)
368{
369 Quaternion q1(1.0f, 2.0f, 3.0f, 4.0f);
370 EXPECT_EQ(float4(conjugate(q1)), float4(1.0f, -2.0f, -3.0f, -4.0f));
371}
372
373TEST(math_rotation, QuaternionNormalize)
374{
375 Quaternion q1(1.0f, 2.0f, 3.0f, 4.0f);
376 EXPECT_V4_NEAR(float4(normalize(q1)),
377 float4(0.1825741827, 0.3651483654, 0.5477225780, 0.7302967309),
378 1e-6f);
379}
380
381TEST(math_rotation, QuaternionInvert)
382{
383 Quaternion q1(1.0f, 2.0f, 3.0f, 4.0f);
384 EXPECT_V4_NEAR(float4(invert(q1)), float4(0.0333333f, -0.0666667f, -0.1f, -0.133333f), 1e-4f);
385
386 Quaternion q2(0.927091f, 0.211322f, -0.124857f, 0.283295f);
388 EXPECT_V4_NEAR(float4(result), float4(0.927091f, -0.211322f, 0.124857f, -0.283295f), 1e-4f);
389}
390
391TEST(math_rotation, QuaternionCanonicalize)
392{
393 EXPECT_V4_NEAR(float4(canonicalize(Quaternion(0.5f, 2.0f, 3.0f, 4.0f))),
394 float4(0.5f, 2.0f, 3.0f, 4.0f),
395 1e-4f);
396 EXPECT_V4_NEAR(float4(canonicalize(Quaternion(-0.5f, 2.0f, 3.0f, 4.0f))),
397 float4(0.5f, -2.0f, -3.0f, -4.0f),
398 1e-4f);
399}
400
401TEST(math_rotation, QuaternionAngleBetween)
402{
403 Quaternion q1 = normalize(Quaternion(0.927091f, 0.211322f, -0.124857f, 0.283295f));
404 Quaternion q2 = normalize(Quaternion(-0.083377f, -0.051681f, 0.498261f, -0.86146f));
405 Quaternion q3 = rotation_between(q1, q2);
406 EXPECT_V4_NEAR(float4(q3), float4(-0.394478f, 0.00330195f, 0.284119f, -0.873872f), 1e-4f);
407 EXPECT_NEAR(float(angle_of(q1)), 0.76844f, 1e-4f);
408 EXPECT_NEAR(float(angle_of(q2)), 3.30854f, 1e-4f);
409 EXPECT_NEAR(float(angle_of(q3)), 3.95259f, 1e-4f);
410 EXPECT_NEAR(float(angle_of_signed(q1)), 0.76844f, 1e-4f);
411 EXPECT_NEAR(float(angle_of_signed(q2)), 3.30854f - 2 * M_PI, 1e-4f);
412 EXPECT_NEAR(float(angle_of_signed(q3)), 3.95259f - 2 * M_PI, 1e-4f);
413 EXPECT_NEAR(float(angle_between(q1, q2)), 3.95259f, 1e-4f);
414 EXPECT_NEAR(float(angle_between_signed(q1, q2)), 3.95259f - 2 * M_PI, 1e-4f);
415}
416
417TEST(math_rotation, QuaternionPower)
418{
419 Quaternion q1 = normalize(Quaternion(0.927091f, 0.211322f, -0.124857f, 0.283295f));
420 Quaternion q2 = normalize(Quaternion(-0.083377f, -0.051681f, 0.498261f, -0.86146f));
421
422 EXPECT_V4_NEAR(
423 float4(math::pow(q1, -2.5f)), float4(0.573069, -0.462015, 0.272976, -0.61937), 1e-4);
424 EXPECT_V4_NEAR(
425 float4(math::pow(q1, -0.5f)), float4(0.981604, -0.107641, 0.0635985, -0.144302), 1e-4);
426 EXPECT_V4_NEAR(
427 float4(math::pow(q1, 0.5f)), float4(0.981604, 0.107641, -0.0635985, 0.144302), 1e-4);
428 EXPECT_V4_NEAR(
429 float4(math::pow(q1, 2.5f)), float4(0.573069, 0.462015, -0.272976, 0.61937), 1e-4);
430 EXPECT_V4_NEAR(
431 float4(math::pow(q2, -2.5f)), float4(-0.545272, -0.0434735, 0.419131, -0.72465), 1e-4);
432 EXPECT_V4_NEAR(
433 float4(math::pow(q2, -0.5f)), float4(0.676987, 0.0381699, -0.367999, 0.636246), 1e-4);
434 EXPECT_V4_NEAR(
435 float4(math::pow(q2, 0.5f)), float4(0.676987, -0.0381699, 0.367999, -0.636246), 1e-4);
436 EXPECT_V4_NEAR(
437 float4(math::pow(q2, 2.5f)), float4(-0.545272, 0.0434735, -0.419131, 0.72465), 1e-4);
438}
439
440TEST(math_rotation, QuaternionFromTriangle)
441{
442 float3 v1(0.927091f, 0.211322f, -0.124857f);
443 float3 v2(-0.051681f, 0.498261f, -0.86146f);
444 float3 v3(0.211322f, -0.124857f, 0.283295f);
445 EXPECT_V4_NEAR(float4(from_triangle(v1, v2, v3)),
446 float4(0.255566f, -0.213799f, 0.454253f, 0.826214f),
447 1e-5f);
448 EXPECT_V4_NEAR(float4(from_triangle(v1, v3, v2)),
449 float4(0.103802f, 0.295067f, -0.812945f, 0.491204f),
450 1e-5f);
451}
452
453TEST(math_rotation, QuaternionFromVector)
454{
455 float3 v1(0.927091f, 0.211322f, -0.124857f);
456 float3 v2(-0.051681f, 0.498261f, -0.86146f);
457 EXPECT_V4_NEAR(float4(from_vector(v1, AxisSigned::X_POS, Axis::X)),
458 float4(0.129047, 0, -0.50443, -0.853755),
459 1e-5f);
460 EXPECT_V4_NEAR(float4(from_vector(v1, AxisSigned::X_POS, Axis::Y)),
461 float4(0.12474, 0.0330631, -0.706333, -0.696017),
462 1e-5f);
463 EXPECT_V4_NEAR(float4(from_vector(v1, AxisSigned::X_POS, Axis::Z)),
464 float4(0.111583, -0.0648251, -0.00729451, -0.991612),
465 1e-5f);
466 EXPECT_V4_NEAR(float4(from_vector(v1, AxisSigned::Y_POS, Axis::X)),
467 float4(0.476074, 0.580363, -0.403954, 0.522832),
468 1e-5f);
469 EXPECT_V4_NEAR(float4(from_vector(v1, AxisSigned::Y_POS, Axis::Y)),
470 float4(0.62436, 0.104259, 0, 0.774148),
471 1e-5f);
472 EXPECT_V4_NEAR(float4(from_vector(v1, AxisSigned::Y_POS, Axis::Z)),
473 float4(0.622274, 0.0406802, 0.0509963, 0.780077),
474 1e-5f);
475 EXPECT_V4_NEAR(float4(from_vector(v1, AxisSigned::Z_POS, Axis::X)),
476 float4(0.747014, 0.0737433, -0.655337, 0.0840594),
477 1e-5f);
478 EXPECT_V4_NEAR(float4(from_vector(v1, AxisSigned::Z_POS, Axis::Z)),
479 float4(0.751728, 0.146562, -0.642981, 0),
480 1e-5f);
481 EXPECT_V4_NEAR(float4(from_vector(v1, AxisSigned::Z_POS, Axis::Z)),
482 float4(0.751728, 0.146562, -0.642981, 0),
483 1e-5f);
484 EXPECT_V4_NEAR(float4(from_vector(v1, AxisSigned::X_NEG, Axis::X)),
485 float4(0.991638, 0, 0.0656442, 0.111104),
486 1e-5f);
487 EXPECT_V4_NEAR(float4(from_vector(v1, AxisSigned::X_NEG, Axis::Y)),
488 float4(0.706333, 0.696017, 0.12474, 0.0330631),
489 1e-5f);
490 EXPECT_V4_NEAR(float4(from_vector(v1, AxisSigned::X_NEG, Axis::Z)),
491 float4(0.991612, -0.0072946, 0.0648251, 0.111583),
492 1e-5f);
493 EXPECT_V4_NEAR(float4(from_vector(v1, AxisSigned::Y_NEG, Axis::X)),
494 float4(0.580363, -0.476074, -0.522832, -0.403954),
495 1e-5f);
496 EXPECT_V4_NEAR(float4(from_vector(v1, AxisSigned::Y_NEG, Axis::Y)),
497 float4(0.781137, -0.083334, 0, -0.618774),
498 1e-5f);
499 EXPECT_V4_NEAR(float4(from_vector(v1, AxisSigned::Y_NEG, Axis::Z)),
500 float4(0.780077, -0.0509963, 0.0406802, -0.622274),
501 1e-5f);
502 EXPECT_V4_NEAR(float4(from_vector(v1, AxisSigned::Z_NEG, Axis::X)),
503 float4(0.0737433, -0.747014, -0.0840594, -0.655337),
504 1e-5f);
505 EXPECT_V4_NEAR(float4(from_vector(v1, AxisSigned::Z_NEG, Axis::Z)),
506 float4(0.659473, -0.167065, 0.732929, 0),
507 1e-5f);
508 EXPECT_V4_NEAR(float4(from_vector(v1, AxisSigned::Z_NEG, Axis::Z)),
509 float4(0.659473, -0.167065, 0.732929, 0),
510 1e-5f);
511
512 EXPECT_V4_NEAR(float4(from_vector(v2, AxisSigned::X_POS, Axis::X)),
513 float4(0.725211, 0, -0.596013, -0.344729),
514 1e-5f);
515 EXPECT_V4_NEAR(float4(from_vector(v2, AxisSigned::X_POS, Axis::Y)),
516 float4(0.691325, 0.219092, -0.672309, -0.148561),
517 1e-5f);
518 EXPECT_V4_NEAR(float4(from_vector(v2, AxisSigned::X_POS, Axis::Z)),
519 float4(0.643761, -0.333919, -0.370346, -0.580442),
520 1e-5f);
521 EXPECT_V4_NEAR(float4(from_vector(v2, AxisSigned::Y_POS, Axis::X)),
522 float4(0.320473, 0.593889, 0.383792, 0.630315),
523 1e-5f);
524 EXPECT_V4_NEAR(float4(from_vector(v2, AxisSigned::Y_POS, Axis::Y)),
525 float4(0.499999, 0.864472, 0, -0.0518617),
526 1e-5f);
527 EXPECT_V4_NEAR(float4(from_vector(v2, AxisSigned::Y_POS, Axis::Z)),
528 float4(0.0447733, 0.0257574, -0.49799, -0.865643),
529 1e-5f);
530 EXPECT_V4_NEAR(float4(from_vector(v2, AxisSigned::Z_POS, Axis::X)),
531 float4(0.646551, 0.193334, -0.174318, 0.717082),
532 1e-5f);
533 EXPECT_V4_NEAR(float4(from_vector(v2, AxisSigned::Z_POS, Axis::Z)),
534 float4(0.965523, 0.258928, 0.0268567, 0),
535 1e-5f);
536 EXPECT_V4_NEAR(float4(from_vector(v2, AxisSigned::Z_POS, Axis::Z)),
537 float4(0.965523, 0.258928, 0.0268567, 0),
538 1e-5f);
539 EXPECT_V4_NEAR(float4(from_vector(v2, AxisSigned::X_NEG, Axis::X)),
540 float4(0.688527, 0, 0.627768, 0.363095),
541 1e-5f);
542 EXPECT_V4_NEAR(float4(from_vector(v2, AxisSigned::X_NEG, Axis::Y)),
543 float4(0.672309, 0.148561, 0.691325, 0.219092),
544 1e-5f);
545 EXPECT_V4_NEAR(float4(from_vector(v2, AxisSigned::X_NEG, Axis::Z)),
546 float4(0.580442, -0.370345, 0.333919, 0.643761),
547 1e-5f);
548 EXPECT_V4_NEAR(float4(from_vector(v2, AxisSigned::Y_NEG, Axis::X)),
549 float4(0.593889, -0.320473, -0.630315, 0.383792),
550 1e-5f);
551 EXPECT_V4_NEAR(float4(from_vector(v2, AxisSigned::Y_NEG, Axis::Y)),
552 float4(0.866026, -0.499102, 0, 0.0299423),
553 1e-5f);
554 EXPECT_V4_NEAR(float4(from_vector(v2, AxisSigned::Y_NEG, Axis::Z)),
555 float4(0.865643, -0.49799, -0.0257574, 0.0447733),
556 1e-5f);
557 EXPECT_V4_NEAR(float4(from_vector(v2, AxisSigned::Z_NEG, Axis::X)),
558 float4(0.193334, -0.646551, -0.717082, -0.174318),
559 1e-5f);
560 EXPECT_V4_NEAR(float4(from_vector(v2, AxisSigned::Z_NEG, Axis::Z)),
561 float4(0.260317, -0.960371, -0.0996123, 0),
562 1e-5f);
563 EXPECT_V4_NEAR(float4(from_vector(v2, AxisSigned::Z_NEG, Axis::Z)),
564 float4(0.260317, -0.960371, -0.0996123, 0),
565 1e-5f);
566}
567
568TEST(math_rotation, QuaternionWrappedAround)
569{
570 Quaternion q1 = normalize(Quaternion(0.927091f, 0.211322f, -0.124857f, 0.283295f));
571 Quaternion q2 = normalize(Quaternion(-0.083377f, -0.051681f, 0.498261f, -0.86146f));
572 Quaternion q_malformed = Quaternion(0.0f, 0.0f, 0.0f, 0.0f);
573 EXPECT_V4_NEAR(float4(q1.wrapped_around(q2)), float4(-q1), 1e-4f);
574 EXPECT_V4_NEAR(float4(q1.wrapped_around(-q2)), float4(q1), 1e-4f);
575 EXPECT_V4_NEAR(float4(q1.wrapped_around(q_malformed)), float4(q1), 1e-4f);
576}
577
578TEST(math_rotation, QuaternionFromTracking)
579{
580 for (int i : IndexRange(6)) {
581 for (int j : IndexRange(3)) {
582 AxisSigned forward_axis = AxisSigned::from_int(i);
583 Axis up_axis = Axis::from_int(j);
584
585 if (forward_axis.axis() == up_axis) {
586 continue;
587 }
588
590 quat_apply_track(&expect.w, forward_axis.as_int(), up_axis.as_int());
591
592 /* This is the expected axis conversion for curve tangent space to tracked object space. */
595 from_orthonormal_axes(forward_axis, AxisSigned(up_axis)));
597
598 EXPECT_V4_NEAR(float4(result), float4(expect), 1e-5f);
599 }
600 }
601}
602
603TEST(math_rotation, EulerWrappedAround)
604{
605 EulerXYZ eul1 = EulerXYZ(2.08542, -1.12485, -1.23738);
606 EulerXYZ eul2 = EulerXYZ(4.06112, 0.561928, -18.9063);
607 EXPECT_V3_NEAR(float3(eul1.wrapped_around(eul2)), float3(2.08542, -1.12485, -20.0869), 1e-4f);
608 EXPECT_V3_NEAR(float3(eul2.wrapped_around(eul1)), float3(4.06112, 0.561928, -0.0567436), 1e-4f);
609}
610
611TEST(math_rotation, Euler3ToGimbal)
612{
613 /* All the same rotation. */
614 float3 ijk{0.350041, -0.358896, 0.528994};
615 Euler3 euler3_xyz(ijk, EulerOrder::XYZ);
616 Euler3 euler3_xzy(ijk, EulerOrder::XZY);
617 Euler3 euler3_yxz(ijk, EulerOrder::YXZ);
618 Euler3 euler3_yzx(ijk, EulerOrder::YZX);
619 Euler3 euler3_zxy(ijk, EulerOrder::ZXY);
620 Euler3 euler3_zyx(ijk, EulerOrder::ZYX);
621
622 float3x3 mat_xyz = transpose(
623 float3x3({0.808309, -0.504665, 0}, {0.47251, 0.863315, 0}, {0.351241, 0, 1}));
624 float3x3 mat_xzy = transpose(
625 float3x3({0.808309, 0, -0.351241}, {0.504665, 1, -0}, {0.303232, 0, 0.936285}));
626 float3x3 mat_yxz = transpose(
627 float3x3({0.863315, -0.474062, 0}, {0.504665, 0.810963, 0}, {-0, 0.342936, 1}));
628 float3x3 mat_yzx = transpose(
629 float3x3({1, -0.504665, 0}, {0, 0.810963, -0.342936}, {0, 0.296062, 0.939359}));
630 float3x3 mat_zxy = transpose(
631 float3x3({0.936285, 0, -0.329941}, {0, 1, -0.342936}, {0.351241, 0, 0.879508}));
632 float3x3 mat_zyx = transpose(
633 float3x3({1, -0, -0.351241}, {0, 0.939359, -0.321086}, {0, 0.342936, 0.879508}));
634
635 EXPECT_M3_NEAR(to_gimbal_axis(euler3_xyz), mat_xyz, 1e-4);
636 EXPECT_M3_NEAR(to_gimbal_axis(euler3_xzy), mat_xzy, 1e-4);
637 EXPECT_M3_NEAR(to_gimbal_axis(euler3_yxz), mat_yxz, 1e-4);
638 EXPECT_M3_NEAR(to_gimbal_axis(euler3_yzx), mat_yzx, 1e-4);
639 EXPECT_M3_NEAR(to_gimbal_axis(euler3_zxy), mat_zxy, 1e-4);
640 EXPECT_M3_NEAR(to_gimbal_axis(euler3_zyx), mat_zyx, 1e-4);
641}
642
643TEST(math_rotation, CartesianBasis)
644{
645 for (int i : IndexRange(6)) {
646 for (int j : IndexRange(6)) {
647 for (int k : IndexRange(6)) {
648 for (int l : IndexRange(6)) {
649 AxisSigned src_forward = AxisSigned::from_int(i);
651 AxisSigned dst_forward = AxisSigned::from_int(k);
653
654 if ((abs(src_forward) == abs(src_up)) || (abs(dst_forward) == abs(dst_up))) {
655 /* Assertion expected. */
656 continue;
657 }
658
659 float3x3 expect;
660 if (src_forward == dst_forward && src_up == dst_up) {
661 expect = float3x3::identity();
662 }
663 else {
664 /* TODO: Find a way to test without resorting to old C API. */
665 mat3_from_axis_conversion(src_forward.as_int(),
666 src_up.as_int(),
667 dst_forward.as_int(),
668 dst_up.as_int(),
669 expect.ptr());
670 }
671
672 CartesianBasis rotation = rotation_between(from_orthonormal_axes(src_forward, src_up),
673 from_orthonormal_axes(dst_forward, dst_up));
674 EXPECT_EQ(from_rotation<float3x3>(rotation), expect);
675
676 if (src_forward == dst_forward) {
677 expect = float3x3::identity();
678 }
679 else {
680 /* TODO: Find a way to test without resorting to old C API. */
682 src_forward.as_int(), dst_forward.as_int(), expect.ptr());
683 }
684
685 EXPECT_EQ(from_rotation<float3x3>(rotation_between(src_forward, dst_forward)), expect);
686
687 float3 point(1.0f, 2.0f, 3.0f);
688 CartesianBasis rotation_inv = invert(rotation);
689 /* Test inversion identity. */
690 EXPECT_EQ(transform_point(rotation_inv, transform_point(rotation, point)), point);
691 }
692 }
693 }
694 }
695}
696
697TEST(math_rotation, Transform)
698{
699 Quaternion q(0.927091f, 0.211322f, -0.124857f, 0.283295f);
700
701 float3 p(0.576f, -0.6546f, 46.354f);
703 EXPECT_V3_NEAR(result, float3(-4.33722f, -21.661f, 40.7608f), 1e-4f);
704
705 /* Validated using `to_quaternion` before doing the transform. */
706 float3 p2(1.0f, 2.0f, 3.0f);
708 p2);
709 EXPECT_EQ(result, float3(1.0f, 2.0f, 3.0f));
713 p2);
714 EXPECT_EQ(result, float3(-2.0f, 1.0f, 3.0f));
716 EXPECT_EQ(result, float3(3.0f, 1.0f, 2.0f));
718 EXPECT_EQ(result, float3(-2.0f, 3.0f, -1.0f));
719}
720
721TEST(math_rotation, DualQuaternionNormalize)
722{
723 DualQuaternion sum = DualQuaternion(Quaternion(0, 0, 1, 0), Quaternion(0, 1, 0, 1)) * 2.0f;
724 sum += DualQuaternion(Quaternion(1, 0, 0, 0), Quaternion(1, 1, 1, 1), float4x4::identity()) *
725 4.0f;
726 sum += DualQuaternion(Quaternion(1, 0, 0, 0), Quaternion(1, 0, 0, 0), float4x4::identity()) *
727 3.0f;
728
729 sum = normalize(sum);
730
731 /* The difference with the C API. */
732 float len = length(float4(0.777778, 0, 0.222222, 0));
733
734 EXPECT_V4_NEAR(float4(sum.quat), (float4(0.777778, 0, 0.222222, 0) / len), 1e-4f);
735 EXPECT_V4_NEAR(float4(sum.trans), (float4(0.777778, 0.666667, 0.444444, 0.666667) / len), 1e-4f);
737 EXPECT_EQ(sum.scale_weight, 1.0f);
738 EXPECT_EQ(sum.quat_weight, 1.0f);
739}
740
741TEST(math_rotation, DualQuaternionFromMatrix)
742{
743 {
744 float4x4 mat{transpose(float4x4({-2.14123, -0.478481, -1.38296, -2.26029},
745 {-1.28264, 2.87361, 0.0230992, 12.8871},
746 {3.27343, 0.812993, -0.895575, -13.5216},
747 {0, 0, 0, 1}))};
748 float4x4 basemat{transpose(float4x4({0.0988318, 0.91328, 0.39516, 7.73971},
749 {0.16104, -0.406549, 0.899324, 22.8871},
750 {0.981987, -0.0252451, -0.187255, -3.52155},
751 {0, 0, 0, 1}))};
752 float4x4 expected_scale_mat{transpose(float4x4({4.08974, 0.306437, -0.0853435, -31.2277},
753 {-0.445021, 2.97151, -0.250095, -42.5586},
754 {0.146173, 0.473002, 1.62645, -9.75092},
755 {0, 0, 0, 1}))};
756
757 DualQuaternion dq = to_dual_quaternion(mat, basemat);
758 EXPECT_V4_NEAR(float4(dq.quat), float4(0.502368, 0.0543716, -0.854483, -0.120535), 1e-4f);
759 EXPECT_V4_NEAR(float4(dq.trans), float4(22.674, -0.878616, 11.2762, 14.167), 1e-4f);
760 EXPECT_M4_NEAR(dq.scale, expected_scale_mat, 1e-4f);
761 EXPECT_EQ(dq.scale_weight, 1.0f);
762 EXPECT_EQ(dq.quat_weight, 1.0f);
763 }
764 {
765 float4x4 mat{transpose(float4x4({-0.0806635, -1.60529, 2.44763, 26.823},
766 {-1.04583, -0.150756, -0.385074, -22.2225},
767 {-0.123402, 2.32698, 1.66357, 5.397},
768 {0, 0, 0, 1}))};
769 float4x4 basemat{transpose(float4x4({0.0603774, 0.904674, 0.421806, 36.823},
770 {-0.271734, 0.421514, -0.865151, -12.2225},
771 {-0.960477, -0.0623834, 0.27128, 15.397},
772 {0, 0, 0, 1}))};
773 float4x4 expected_scale_mat{transpose(float4x4({0.248852, 2.66363, -0.726295, 71.3985},
774 {0.971507, -0.382422, 1.09917, -69.5943},
775 {-0.331274, 0.8794, 2.67787, -2.88715},
776 {0, 0, 0, 1}))};
777
778 DualQuaternion dq = to_dual_quaternion(mat, basemat);
779 EXPECT_V4_NEAR(float4(dq.quat), float4(0.149898, -0.319339, -0.0441496, -0.934668), 1e-4f);
780 EXPECT_V4_NEAR(float4(dq.trans), float4(-2.20019, 39.6236, 49.052, -16.2077), 1e-4f);
781 EXPECT_M4_NEAR(dq.scale, expected_scale_mat, 1e-4f);
782 EXPECT_EQ(dq.scale_weight, 1.0f);
783 EXPECT_EQ(dq.quat_weight, 1.0f);
784 }
785
786#if 0 /* Generate random matrices. */
787 for (int i = 0; i < 1000; i++) {
788 auto frand = []() { return (std::rand() - RAND_MAX / 2) / float(RAND_MAX); };
790 float3{frand(), frand(), frand()} * 100.0f,
791 EulerXYZ{frand() * 10.0f, frand() * 10.0f, frand() * 10.0f},
792 float3{frand(), frand(), frand()} * 10.0f);
794 mat.location() + 10, EulerXYZ{frand() * 10.0f, frand() * 10.0f, frand() * 10.0f});
795
796 DualQuaternion expect;
797 mat4_to_dquat((DualQuat *)&expect.quat.w, basemat.ptr(), mat.ptr());
798
799 DualQuaternion dq = to_dual_quaternion(mat, basemat);
800 EXPECT_V4_NEAR(float4(dq.quat), float4(expect.quat), 1e-4f);
801 EXPECT_V4_NEAR(float4(dq.trans), float4(expect.trans), 1e-4f);
802 EXPECT_M4_NEAR(dq.scale, expect.scale, 2e-4f);
804 }
805#endif
806}
807
808TEST(math_rotation, DualQuaternionTransform)
809{
810 {
811 float4x4 scale_mat{transpose(float4x4({4.08974, 0.306437, -0.0853435, -31.2277},
812 {-0.445021, 2.97151, -0.250095, -42.5586},
813 {0.146173, 0.473002, 1.62645, -9.75092},
814 {0, 0, 0, 1}))};
815
816 DualQuaternion dq({0.502368, 0.0543716, -0.854483, -0.120535},
817 {22.674, -0.878616, 11.2762, 14.167},
818 scale_mat);
819
820 float3 p0{51.0f, 1647.0f, 12.0f};
821 float3 p1{58.0f, 0.0054f, 10.0f};
822 float3 p2{0.0f, 7854.0f, 111.0f};
823
824 float3x3 crazy_space_mat;
825 float3 p0_expect = p0;
826 float3 p1_expect = p1;
827 float3 p2_expect = p2;
828 mul_v3m3_dq(p0_expect, crazy_space_mat.ptr(), (DualQuat *)&dq);
829 mul_v3m3_dq(p1_expect, crazy_space_mat.ptr(), (DualQuat *)&dq);
830 mul_v3m3_dq(p2_expect, crazy_space_mat.ptr(), (DualQuat *)&dq);
831
832 float3 p0_result = transform_point(dq, p0);
833 float3 p1_result = transform_point(dq, p1);
834 float3 p2_result = transform_point(dq, p2, &crazy_space_mat);
835
836 float4x4 expected_crazy_space_mat{transpose(float3x3({-2.14123, -0.478481, -1.38296},
837 {-1.28264, 2.87361, 0.0230978},
838 {3.27343, 0.812991, -0.895574}))};
839
840 EXPECT_V3_NEAR(p0_result, p0_expect, 1e-2f);
841 EXPECT_V3_NEAR(p1_result, p1_expect, 1e-2f);
842 EXPECT_V3_NEAR(p2_result, p2_expect, 1e-2f);
843 EXPECT_M3_NEAR(crazy_space_mat, expected_crazy_space_mat, 1e-4f);
844 }
845 {
846 float4x4 scale_mat{transpose(float4x4({0.248852, 2.66363, -0.726295, 71.3985},
847 {0.971507, -0.382422, 1.09917, -69.5943},
848 {-0.331274, 0.8794, 2.67787, -2.88715},
849 {0, 0, 0, 1}))};
850
851 DualQuaternion dq({0.149898, -0.319339, -0.0441496, -0.934668},
852 {-2.20019, 39.6236, 49.052, -16.207},
853 scale_mat);
854
855 float3 p0{51.0f, 1647.0f, 12.0f};
856 float3 p1{58.0f, 0.0054f, 10.0f};
857 float3 p2{0.0f, 7854.0f, 111.0f};
858
859 float3x3 crazy_space_mat;
860 float3 p0_expect = p0;
861 float3 p1_expect = p1;
862 float3 p2_expect = p2;
863 mul_v3m3_dq(p0_expect, crazy_space_mat.ptr(), (DualQuat *)&dq);
864 mul_v3m3_dq(p1_expect, crazy_space_mat.ptr(), (DualQuat *)&dq);
865 mul_v3m3_dq(p2_expect, crazy_space_mat.ptr(), (DualQuat *)&dq);
866
867 float3 p0_result = transform_point(dq, p0);
868 float3 p1_result = transform_point(dq, p1);
869 float3 p2_result = transform_point(dq, p2, &crazy_space_mat);
870
871 float4x4 expected_crazy_space_mat{transpose(float3x3({-0.0806647, -1.60529, 2.44763},
872 {-1.04583, -0.150754, -0.385079},
873 {-0.123401, 2.32698, 1.66357}))};
874
875 EXPECT_V3_NEAR(p0_result, float3(-2591.83, -328.472, 3851.6), 1e-2f);
876 EXPECT_V3_NEAR(p1_result, float3(46.6121, -86.7318, 14.8882), 1e-2f);
877 EXPECT_V3_NEAR(p2_result, float3(-12309.5, -1248.99, 18466.1), 6e-2f);
878 EXPECT_M3_NEAR(crazy_space_mat, expected_crazy_space_mat, 1e-4f);
879 }
880}
881
882TEST(math_axis_angle, AxisAngleFromQuaternion)
883{
884 {
885 const math::AxisAngle axis_angle({0.0f, 1.0f, 0.0f}, math::AngleRadian(0));
886 const math::Quaternion quaternion(1.0f, {0.0f, 0.0f, 0.0f});
887 const math::AxisAngle from_quaternion = math::to_axis_angle(quaternion);
888 EXPECT_V3_NEAR(axis_angle.axis(), from_quaternion.axis(), 1e-6);
889 EXPECT_NEAR(axis_angle.angle().radian(), from_quaternion.angle().radian(), 1e-6);
890 }
891 {
892 const math::AxisAngle axis_angle({0.0f, -1.0f, 0.0f}, math::AngleRadian(0));
893 const math::Quaternion quaternion(-1.0f, {0.0f, 0.0f, 0.0f});
894 const math::AxisAngle from_quaternion = math::to_axis_angle(quaternion);
895 EXPECT_V3_NEAR(axis_angle.axis(), from_quaternion.axis(), 1e-6);
896 EXPECT_NEAR(axis_angle.angle().radian(), from_quaternion.angle().radian(), 1e-6);
897 }
898}
899
900} // namespace blender::math::tests
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
#define M_PI_2
#define M_PI
void unit_m3(float m[3][3])
void quat_to_mat3(float m[3][3], const float q[4])
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])
float dot_qtqt(const float a[4], const float b[4])
float quat_split_swing_and_twist(const float q_in[4], int axis, float r_swing[4], float r_twist[4])
void mat4_to_dquat(DualQuat *dq, const float basemat[4][4], const float mat[4][4])
bool mat3_from_axis_conversion(int src_forward, int src_up, int dst_forward, int dst_up, float r_mat[3][3])
void mul_v3m3_dq(float r[3], float R[3][3], DualQuat *dq)
void quat_apply_track(float quat[4], short axis, short upflag)
void mat3_normalized_to_quat_fast(float q[4], const float mat[3][3])
void mat3_normalized_to_quat(float q[4], const float mat[3][3])
bool mat3_from_axis_conversion_single(int src_axis, int dst_axis, float r_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)
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
static T sum(const btAlignedObjectArray< T > &items)
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
Definition btVector3.h:257
void append_unchecked(const T &value)
void reserve(const int64_t min_capacity)
static constexpr AxisSigned from_int(int axis_int)
constexpr int as_int() const
static constexpr Value Z
static constexpr Axis from_int(const int axis_int)
static constexpr Value X
static constexpr Value Y
nullptr float
#define input
#define sin
#define cos
VecBase< float, D > normalize(VecOp< float, D >) RET
#define abs
MatBase< R, C > transpose(MatBase< C, R >) RET
static float frand()
TEST(math_rotation, DefaultConstructor)
AngleCartesianBase< float > AngleCartesian
QuaternionBase< T > from_triangle(const VecBase< T, 3 > &v1, const VecBase< T, 3 > &v2, const VecBase< T, 3 > &v3, const VecBase< T, 3 > &normal)
AngleRadianBase< float > AngleRadian
QuaternionBase< T > conjugate(const QuaternionBase< T > &a)
QuaternionBase< float > Quaternion
T pow(const T &x, const T &power)
QuaternionBase< T > from_vector(const VecBase< T, 3 > &vector, const AxisSigned track_flag, const Axis up_flag)
QuaternionBase< T > to_quaternion(const AxisAngleBase< T, AngleT > &axis_angle)
CartesianBasis rotation_between(const CartesianBasis &a, const CartesianBasis &b)
AngleRadianBase< T > angle_between(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
MatT from_loc_rot(const typename MatT::loc_type &location, const RotationT &rotation)
AxisAngleBase< float, AngleRadianBase< float > > AxisAngle
AngleRadianBase< T > angle_between_signed(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
EulerXYZBase< float > EulerXYZ
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
MatBase< T, 3, 3 > to_gimbal_axis(const Euler3Base< T > &rotation)
CartesianBasis invert(const CartesianBasis &basis)
float3 rotate_direction_around_axis(const float3 &direction, const float3 &axis, float angle)
AngleRadianBase< T > angle_of(const QuaternionBase< T > &q)
DualQuaternionBase< float > DualQuaternion
QuaternionBase< T > invert_normalized(const QuaternionBase< T > &q)
AxisAngleBase< T, AngleT > to_axis_angle(const EulerXYZBase< T > &euler)
AxisAngleBase< float, AngleCartesianBase< float > > AxisAngleCartesian
QuaternionBase< T > canonicalize(const QuaternionBase< T > &q)
Euler3Base< float > Euler3
AngleRadianBase< T > angle_of_signed(const QuaternionBase< T > &q)
CartesianBasis from_orthonormal_axes(const AxisSigned forward, const AxisSigned up)
MatT from_rotation(const RotationT &rotation)
DualQuaternionBase< T > to_dual_quaternion(const MatBase< T, 4, 4 > &mat, const MatBase< T, 4, 4 > &basemat)
MatT from_loc_rot_scale(const typename MatT::loc_type &location, const RotationT &rotation, const VecBase< typename MatT::base_type, ScaleDim > &scale)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
VecBase< float, 2 > float2
MatBase< float, 3, 3 > float3x3
VecBase< float, 3 > float3
#define fabsf
#define sqrtf
#define sinf
#define cosf
const c_style_mat & ptr() const
EulerXYZBase wrapped_around(const EulerXYZBase &reference) const
QuaternionBase wrapped_around(const QuaternionBase &reference) const
i
Definition text_draw.cc:230
uint len