diff options
author | Campbell Barton <campbell@blender.org> | 2022-07-27 12:48:17 +0300 |
---|---|---|
committer | Campbell Barton <campbell@blender.org> | 2022-07-28 02:39:54 +0300 |
commit | 8d4fa03e5c87e8f83ef19557d673625766d5a148 (patch) | |
tree | 5717ba3908cacdded0992e587868148f226dc2a9 /source/blender/blenlib/tests | |
parent | ff048f5d27c4b785f5d6807eb14416ff308ca5ef (diff) |
BLI_math: improve symmetrical values from sin_cos_from_fraction
When plotting equally distant points around a circle support an extra
axis of symmetry so twice as many exact values are repeated than
originally added in [0], see code-comments for a detailed explanation.
Tests to ensure accuracy and exact symmetry have been added too.
Follow up on fix for T87779.
[0]: 087f27a52f7857887e90754d87a7a73715ebc3fb
Diffstat (limited to 'source/blender/blenlib/tests')
-rw-r--r-- | source/blender/blenlib/tests/BLI_math_rotation_test.cc | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/source/blender/blenlib/tests/BLI_math_rotation_test.cc b/source/blender/blenlib/tests/BLI_math_rotation_test.cc index a283118bea2..460cfd2d36c 100644 --- a/source/blender/blenlib/tests/BLI_math_rotation_test.cc +++ b/source/blender/blenlib/tests/BLI_math_rotation_test.cc @@ -7,6 +7,8 @@ #include "BLI_math_rotation.hh" #include "BLI_math_vector.hh" +#include "BLI_vector.hh" + #include <cmath> /* Test that quaternion converts to itself via matrix. */ @@ -150,6 +152,107 @@ TEST(math_rotation, quat_split_swing_and_twist_negative) EXPECT_V4_NEAR(twist, expected_twist, FLT_EPSILON); } +/* -------------------------------------------------------------------- */ +/** \name Test `sin_cos_from_fraction` Accuracy & Exact Symmetry + * \{ */ + +static void test_sin_cos_from_fraction_accuracy(const int range, const float expected_eps) +{ + for (int i = 0; i < range; i++) { + float sin_cos_fl[2]; + sin_cos_from_fraction(i, range, &sin_cos_fl[0], &sin_cos_fl[1]); + const float phi = (float)(2.0 * M_PI) * ((float)i / (float)range); + const float sin_cos_test_fl[2] = {sinf(phi), cosf(phi)}; + EXPECT_V2_NEAR(sin_cos_fl, sin_cos_test_fl, expected_eps); + } +} + +/** Ensure the result of #sin_cos_from_fraction match #sinf & #cosf. */ +TEST(math_rotation, sin_cos_from_fraction_accuracy) +{ + for (int range = 1; range <= 64; range++) { + test_sin_cos_from_fraction_accuracy(range, 1e-6f); + } +} + +/** Ensure values are exactly symmetrical where possible. */ +static void test_sin_cos_from_fraction_symmetry(const int range) +{ + /* The expected number of unique numbers depends on the range being a multiple of 4/2/1. */ + const enum { + MULTIPLE_OF_1 = 1, + MULTIPLE_OF_2 = 2, + MULTIPLE_OF_4 = 3, + } multiple_of = (range & 1) ? MULTIPLE_OF_1 : ((range & 3) ? MULTIPLE_OF_2 : MULTIPLE_OF_4); + + blender::Vector<blender::float2> coords; + coords.reserve(range); + for (int i = 0; i < range; i++) { + float sin_cos_fl[2]; + sin_cos_from_fraction(i, range, &sin_cos_fl[0], &sin_cos_fl[1]); + switch (multiple_of) { + case MULTIPLE_OF_1: { + sin_cos_fl[0] = fabsf(sin_cos_fl[0]); + break; + } + case MULTIPLE_OF_2: { + sin_cos_fl[0] = fabsf(sin_cos_fl[0]); + sin_cos_fl[1] = fabsf(sin_cos_fl[1]); + break; + } + case MULTIPLE_OF_4: { + sin_cos_fl[0] = fabsf(sin_cos_fl[0]); + sin_cos_fl[1] = fabsf(sin_cos_fl[1]); + if (sin_cos_fl[0] > sin_cos_fl[1]) { + SWAP(float, sin_cos_fl[0], sin_cos_fl[1]); + } + break; + } + } + coords.append_unchecked(sin_cos_fl); + } + /* Sort, then count unique items. */ + std::sort(coords.begin(), coords.end(), [](const blender::float2 &a, const blender::float2 &b) { + float delta = b[0] - a[0]; + if (delta == 0.0f) { + delta = b[1] - a[1]; + } + return delta > 0.0f; + }); + int unique_coords_count = 1; + if (range > 1) { + int i_prev = 0; + for (int i = 1; i < range; i_prev = i++) { + if (coords[i_prev] != coords[i]) { + unique_coords_count += 1; + } + } + } + switch (multiple_of) { + case MULTIPLE_OF_1: { + EXPECT_EQ(unique_coords_count, (range / 2) + 1); + break; + } + case MULTIPLE_OF_2: { + EXPECT_EQ(unique_coords_count, (range / 4) + 1); + break; + } + case MULTIPLE_OF_4: { + EXPECT_EQ(unique_coords_count, (range / 8) + 1); + break; + } + } +} + +TEST(math_rotation, sin_cos_from_fraction_symmetry) +{ + for (int range = 1; range <= 64; range++) { + test_sin_cos_from_fraction_symmetry(range); + } +} + +/** \} */ + namespace blender::math::tests { TEST(math_rotation, RotateDirectionAroundAxis) |