Blender V4.3
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];
195 mat3_normalized_to_quat_fast(actual_quat, input);
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/* -------------------------------------------------------------------- */
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
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));
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)));
596 Quaternion result = to_quaternion<float>(axes);
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);
702 float3 result = transform_point(q, p);
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));
710 result = transform_point(
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)
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
btMatrix3x3 transpose() const
Return the transpose of the matrix.
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 btVector3 & normalize()
Normalize this vector x^2 + y^2 + z^2 = 1.
Definition btVector3.h:303
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
local_group_size(16, 16) .push_constant(Type b
#define sinf(x)
#define cosf(x)
#define fabsf(x)
#define sqrtf(x)
int len
draw_view in_light_buf[] float
IndexRange range
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 cos(const AngleRadianBase< T > &a)
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)
QuaternionBase< T > canonicalize(const QuaternionBase< T > &q)
T sin(const AngleRadianBase< T > &a)
T abs(const T &a)
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
MatBase< float, 3, 3 > float3x3
VecBase< float, 3 > float3
const c_style_mat & ptr() const
EulerXYZBase wrapped_around(const EulerXYZBase &reference) const