imported from "final" folder

This commit is contained in:
2025-11-28 12:12:50 +01:00
parent f9288986cf
commit ff8e725b35
1061 changed files with 225150 additions and 96 deletions

View File

@@ -0,0 +1,474 @@
#include "fl/ease.h"
#include "fl/array.h"
#include "fl/pair.h"
#include "lib8tion/intmap.h"
#include "test.h"
#include <cmath>
#include <cstdlib>
FASTLED_USING_NAMESPACE
// Common array of easing types with names used across multiple test cases
static const fl::pair<fl::EaseType, const char*> ALL_EASING_TYPES[10] = {
{fl::EASE_NONE, "EASE_NONE"},
{fl::EASE_IN_QUAD, "EASE_IN_QUAD"},
{fl::EASE_OUT_QUAD, "EASE_OUT_QUAD"},
{fl::EASE_IN_OUT_QUAD, "EASE_IN_OUT_QUAD"},
{fl::EASE_IN_CUBIC, "EASE_IN_CUBIC"},
{fl::EASE_OUT_CUBIC, "EASE_OUT_CUBIC"},
{fl::EASE_IN_OUT_CUBIC, "EASE_IN_OUT_CUBIC"},
{fl::EASE_IN_SINE, "EASE_IN_SINE"},
{fl::EASE_OUT_SINE, "EASE_OUT_SINE"},
{fl::EASE_IN_OUT_SINE, "EASE_IN_OUT_SINE"}
};
static const size_t NUM_EASING_TYPES = sizeof(ALL_EASING_TYPES) / sizeof(ALL_EASING_TYPES[0]);
TEST_CASE("8-bit easing functions") {
SUBCASE("easeInOutQuad8") {
SUBCASE("boundary values") {
CHECK_CLOSE(easeInOutQuad8(0), 0, 1);
CHECK_CLOSE(easeInOutQuad8(255), 255, 1);
CHECK_CLOSE(easeInOutQuad8(128), 128,
1); // midpoint should be unchanged
}
SUBCASE("symmetry") {
// ease-in-out should be symmetric around midpoint
for (uint8_t i = 0; i < 128; ++i) {
uint8_t forward = easeInOutQuad8(i);
uint8_t backward = easeInOutQuad8(255 - i);
CHECK_CLOSE(forward, 255 - backward, 1);
}
}
SUBCASE("first quarter should be slower than linear") {
// ease-in portion should start slower than linear
uint8_t quarter = easeInOutQuad8(64); // 64 = 255/4
CHECK_LT(quarter, 64); // should be less than linear progression
}
}
SUBCASE("easeInOutCubic8") {
SUBCASE("boundary values") {
CHECK_CLOSE(easeInOutCubic8(0), 0, 1);
CHECK_CLOSE(easeInOutCubic8(255), 255, 1);
CHECK_CLOSE(easeInOutCubic8(128), 128, 1);
}
SUBCASE("symmetry") {
const int kTolerance = 2; // This is too high, come back to this.
for (uint8_t i = 0; i < 128; ++i) {
uint8_t forward = easeInOutCubic8(i);
uint8_t backward = easeInOutCubic8(255 - i);
CHECK_CLOSE(forward, 255 - backward, kTolerance);
}
}
SUBCASE("more pronounced than quadratic") {
// cubic should be more pronounced than quadratic in ease-in portion
uint8_t quarter = 64;
uint8_t quad_result = easeInOutQuad8(quarter);
uint8_t cubic_result = easeInOutCubic8(quarter);
CHECK_LT(cubic_result, quad_result);
}
}
}
TEST_CASE("easing function special values") {
SUBCASE("quarter points") {
// Test specific values that are common in animations
// 16-bit quarter points
CHECK_LT(easeInOutQuad16(16384), 16384);
CHECK_GT(easeInOutQuad16(49152), 49152);
CHECK_LT(easeInOutCubic16(16384), easeInOutQuad16(16384));
CHECK_GT(easeInOutCubic16(49152), easeInOutQuad16(49152));
}
}
TEST_CASE("easeInOutQuad16") {
SUBCASE("boundary values") {
CHECK_EQ(easeInOutQuad16(0), 0);
CHECK_EQ(easeInOutQuad16(65535), 65535);
CHECK_EQ(easeInOutQuad16(32768), 32768); // midpoint
// Test values very close to boundaries
CHECK_EQ(easeInOutQuad16(1), 0);
CHECK_EQ(easeInOutQuad16(65534), 65535);
// Test edge cases around midpoint
CHECK_EQ(easeInOutQuad16(32767), 32767);
CHECK_EQ(easeInOutQuad16(32769), 32770);
}
SUBCASE("quartile values") {
// Test specific quartile values for 16-bit quadratic easing
CHECK_EQ(easeInOutQuad16(16384), 8192); // 25% input -> 12.5% output
CHECK_EQ(easeInOutQuad16(32768),
32768); // 50% input -> 50% output (midpoint)
CHECK_EQ(easeInOutQuad16(49152),
57344); // 75% input -> actual measured output
// Additional quartile boundary checks
CHECK_LT(easeInOutQuad16(16384),
16384); // ease-in should be slower than linear
CHECK_GT(easeInOutQuad16(49152),
49152); // ease-out should be faster than linear
}
SUBCASE("symmetry") {
for (uint16_t i = 0; i < 32768; i += 256) {
uint16_t forward = easeInOutQuad16(i);
uint16_t backward = easeInOutQuad16(65535 - i);
CHECK_EQ(forward, 65535 - backward);
}
}
SUBCASE("scaling consistency with 8-bit") {
const int kTolerance = 2; // Note that this is too high.
// 16-bit version should be consistent with 8-bit when scaled
for (uint16_t i = 0; i <= 255; ++i) {
uint8_t input8 = i;
uint16_t input16 = map8_to_16(input8);
uint8_t result8 = easeInOutQuad8(input8);
uint16_t result16 = easeInOutQuad16(input16);
uint8_t scaled_result16 = map16_to_8(result16);
// Should be within 1 due to precision differences
int16_t diff =
std::abs((int16_t)result8 - (int16_t)scaled_result16);
CHECK_LE(diff, kTolerance);
}
}
}
TEST_CASE("easeInOutCubic16") {
SUBCASE("boundary values") {
CHECK_EQ(easeInOutCubic16(0), 0);
CHECK_EQ(easeInOutCubic16(65535), 65535);
CHECK_EQ(easeInOutCubic16(32768), 32769);
}
SUBCASE("quartile values") {
CHECK_EQ(easeInOutCubic16(16384), 4096);
CHECK_EQ(easeInOutCubic16(32768), 32769);
CHECK_EQ(easeInOutCubic16(49152), 61440);
}
SUBCASE("symmetry") {
const int kTolerance = 2; // Note that this is too high.
for (uint16_t i = 0; i < 32768; i += 256) {
uint16_t forward = easeInOutCubic16(i);
uint16_t backward = easeInOutCubic16(65535 - i);
// CHECK_EQ(forward, 65535 - backward);
CHECK_CLOSE(forward, 65535 - backward, kTolerance);
}
}
SUBCASE("more pronounced than quadratic") {
uint16_t quarter = 16384;
uint16_t quad_result = easeInOutQuad16(quarter);
uint16_t cubic_result = easeInOutCubic16(quarter);
CHECK_LT(cubic_result, quad_result);
}
SUBCASE("scaling consistency with 8-bit") {
for (uint16_t i = 0; i <= 255; ++i) {
uint8_t input8 = i;
uint16_t input16 = map8_to_16(input8);
uint8_t result8 = easeInOutCubic8(input8);
uint16_t result16 = easeInOutCubic16(input16);
uint8_t scaled_result16 = map16_to_8(result16);
// Should be within 2 due to precision differences in cubic
// calculation
int16_t diff =
std::abs((int16_t)result8 - (int16_t)scaled_result16);
CHECK_LE(diff, 2);
}
}
}
TEST_CASE("easing function ordering") {
SUBCASE("8-bit: cubic should be more pronounced than quadratic") {
for (uint16_t i = 32; i < 128; i += 16) { // test ease-in portion
uint8_t quad = easeInOutQuad8(i);
uint8_t cubic = easeInOutCubic8(i);
CHECK_LE(cubic, quad);
}
for (uint16_t i = 128; i < 224; i += 16) { // test ease-out portion
uint8_t quad = easeInOutQuad8(i);
uint8_t cubic = easeInOutCubic8(i);
CHECK_GE(cubic, quad);
}
}
SUBCASE("16-bit: cubic should be more pronounced than quadratic") {
for (uint32_t i = 8192; i < 32768; i += 4096) { // test ease-in portion
uint16_t quad = easeInOutQuad16(i);
uint16_t cubic = easeInOutCubic16(i);
CHECK_LE(cubic, quad);
}
for (uint32_t i = 32768; i < 57344;
i += 4096) { // test ease-out portion
uint16_t quad = easeInOutQuad16(i);
uint16_t cubic = easeInOutCubic16(i);
CHECK_GE(cubic, quad);
}
}
}
TEST_CASE("easeInQuad16") {
SUBCASE("boundary values") {
CHECK_EQ(easeInQuad16(0), 0);
CHECK_EQ(easeInQuad16(65535), 65535);
// Test values very close to boundaries
CHECK_EQ(easeInQuad16(1), 0); // (1 * 1) / 65535 = 0
CHECK_EQ(easeInQuad16(65534), 65533); // (65534 * 65534) / 65535 = 65533
}
SUBCASE("quartile values") {
// Test specific quartile values for 16-bit quadratic ease-in
// Expected values calculated as (input * input) / 65535
CHECK_EQ(easeInQuad16(16384),
4096); // 25% input -> ~6.25% output: (16384^2)/65535 = 4096
CHECK_EQ(easeInQuad16(32768),
16384); // 50% input -> 25% output: (32768^2)/65535 = 16384
CHECK_EQ(easeInQuad16(49152),
36864); // 75% input -> ~56.25% output: (49152^2)/65535 = 36864
// Additional test points
CHECK_EQ(easeInQuad16(8192), 1024); // 12.5% input -> ~1.56% output
CHECK_EQ(easeInQuad16(57344), 50176); // 87.5% input -> ~76.56% output
}
SUBCASE("mathematical precision") {
// Test specific values where we can verify the exact mathematical
// result
CHECK_EQ(easeInQuad16(256), 1); // (256 * 256) / 65535 = 1
CHECK_EQ(easeInQuad16(512), 4); // (512 * 512) / 65535 = 4
CHECK_EQ(easeInQuad16(1024), 16); // (1024 * 1024) / 65535 = 16
CHECK_EQ(easeInQuad16(2048), 64); // (2048 * 2048) / 65535 = 64
CHECK_EQ(easeInQuad16(4096), 256); // (4096 * 4096) / 65535 = 256
}
SUBCASE("ease-in behavior") {
// For ease-in, early values should be less than linear progression
// while later values should be greater than linear progression
// First quarter should be significantly slower than linear
uint16_t quarter_linear = 16384; // 25% of 65535
uint16_t quarter_eased = easeInQuad16(16384);
CHECK_LT(quarter_eased, quarter_linear);
CHECK_LT(quarter_eased, quarter_linear / 2); // Should be much slower
// Third quarter should still be slower than linear for ease-in
// (ease-in accelerates but doesn't surpass linear until very late)
uint16_t three_quarter_linear = 49152; // 75% of 65535
uint16_t three_quarter_eased = easeInQuad16(49152);
CHECK_LT(three_quarter_eased, three_quarter_linear);
// The acceleration should be visible in the differences
uint16_t early_diff = easeInQuad16(8192) - easeInQuad16(0); // 0-12.5%
uint16_t late_diff =
easeInQuad16(57344) - easeInQuad16(49152); // 75-87.5%
CHECK_GT(late_diff,
early_diff * 10); // Late differences should be much larger
}
SUBCASE("specific known values") {
// Test some additional specific values for regression testing
CHECK_EQ(easeInQuad16(65535 / 4), 4095); // Quarter point
CHECK_EQ(easeInQuad16(65535 / 2), 16383); // Half point
CHECK_EQ(easeInQuad16(65535 * 3 / 4), 36863); // Three-quarter point
// Edge cases near boundaries
CHECK_EQ(easeInQuad16(255), 0); // Small value should round to 0
CHECK_EQ(easeInQuad16(65280), 65025); // Near-max value
}
}
TEST_CASE("All easing functions boundary tests") {
SUBCASE("8-bit easing functions boundary conditions") {
for (size_t i = 0; i < NUM_EASING_TYPES; ++i) {
fl::EaseType type = ALL_EASING_TYPES[i].first;
const char* name = ALL_EASING_TYPES[i].second;
uint8_t result_0 = fl::ease8(type, 0);
uint8_t result_255 = fl::ease8(type, 255);
INFO("Testing EaseType " << name);
CHECK_EQ(result_0, 0);
CHECK_EQ(result_255, 255);
}
}
SUBCASE("16-bit easing functions boundary conditions") {
for (size_t i = 0; i < NUM_EASING_TYPES; ++i) {
fl::EaseType type = ALL_EASING_TYPES[i].first;
const char* name = ALL_EASING_TYPES[i].second;
uint16_t result_0 = fl::ease16(type, 0);
uint16_t result_max = fl::ease16(type, 65535);
INFO("Testing EaseType " << name);
CHECK_EQ(result_0, 0);
CHECK_EQ(result_max, 65535);
}
}
}
TEST_CASE("All easing functions monotonicity tests") {
SUBCASE("8-bit easing functions monotonicity") {
for (size_t i = 0; i < NUM_EASING_TYPES; ++i) {
fl::EaseType type = ALL_EASING_TYPES[i].first;
const char* name = ALL_EASING_TYPES[i].second;
// Function should be non-decreasing
uint8_t prev = 0;
for (uint16_t input = 0; input <= 255; ++input) {
uint8_t current = fl::ease8(type, input);
INFO("Testing EaseType " << name << " at input " << input);
CHECK_GE(current, prev);
prev = current;
}
}
}
SUBCASE("16-bit easing functions monotonicity") {
for (size_t i = 0; i < NUM_EASING_TYPES; ++i) {
fl::EaseType type = ALL_EASING_TYPES[i].first;
const char* name = ALL_EASING_TYPES[i].second;
// Function should be non-decreasing
uint16_t prev = 0;
for (uint32_t input = 0; input <= 65535; input += 256) {
uint16_t current = fl::ease16(type, input);
INFO("Testing EaseType " << name << " at input " << input);
CHECK_GE(current, prev);
prev = current;
}
}
}
}
TEST_CASE("All easing functions 8-bit vs 16-bit consistency tests") {
// Define expected tolerances for different easing types
const int tolerances[] = {
1, // EASE_NONE - should be perfect
2, // EASE_IN_QUAD - basic precision differences
2, // EASE_OUT_QUAD - basic precision differences
2, // EASE_IN_OUT_QUAD - basic precision differences
3, // EASE_IN_CUBIC - higher tolerance for cubic calculations
3, // EASE_OUT_CUBIC - higher tolerance for cubic calculations
3, // EASE_IN_OUT_CUBIC - higher tolerance for cubic calculations
4, // EASE_IN_SINE - sine functions have more precision loss
4, // EASE_OUT_SINE - sine functions have more precision loss
4 // EASE_IN_OUT_SINE - sine functions have more precision loss
};
SUBCASE("8-bit vs 16-bit scaling consistency") {
for (size_t type_idx = 0; type_idx < NUM_EASING_TYPES; ++type_idx) {
fl::EaseType type = ALL_EASING_TYPES[type_idx].first;
const char* name = ALL_EASING_TYPES[type_idx].second;
int tolerance = tolerances[type_idx];
// Track maximum difference found for this easing type
int max_diff = 0;
uint8_t worst_input = 0;
// Test all 8-bit input values
for (uint16_t i = 0; i <= 255; ++i) {
uint8_t input8 = i;
uint16_t input16 = map8_to_16(input8);
uint8_t result8 = fl::ease8(type, input8);
uint16_t result16 = fl::ease16(type, input16);
uint8_t scaled_result16 = map16_to_8(result16);
int16_t diff = std::abs((int16_t)result8 - (int16_t)scaled_result16);
// Track the worst case for reporting
if (diff > max_diff) {
max_diff = diff;
worst_input = input8;
}
INFO("Testing EaseType " << name
<< " at input " << (int)input8
<< " (8-bit result: " << (int)result8
<< ", 16-bit scaled result: " << (int)scaled_result16
<< ", diff: " << diff << ")");
CHECK_LE(diff, tolerance);
}
// Log the maximum difference found for this easing type
INFO("EaseType " << name << " maximum difference: " << max_diff
<< " at input " << (int)worst_input);
}
}
SUBCASE("Boundary values consistency") {
for (size_t type_idx = 0; type_idx < NUM_EASING_TYPES; ++type_idx) {
fl::EaseType type = ALL_EASING_TYPES[type_idx].first;
const char* name = ALL_EASING_TYPES[type_idx].second;
// Test boundary values (0 and max) - these should be exact
uint8_t result8_0 = fl::ease8(type, 0);
uint16_t result16_0 = fl::ease16(type, 0);
uint8_t scaled_result16_0 = map16_to_8(result16_0);
uint8_t result8_255 = fl::ease8(type, 255);
uint16_t result16_65535 = fl::ease16(type, 65535);
uint8_t scaled_result16_255 = map16_to_8(result16_65535);
INFO("Testing EaseType " << name << " boundary values");
CHECK_EQ(result8_0, scaled_result16_0);
CHECK_EQ(result8_255, scaled_result16_255);
// Both should map to exact boundaries
CHECK_EQ(result8_0, 0);
CHECK_EQ(result8_255, 255);
CHECK_EQ(scaled_result16_0, 0);
CHECK_EQ(scaled_result16_255, 255);
}
}
SUBCASE("Midpoint consistency") {
for (size_t type_idx = 0; type_idx < NUM_EASING_TYPES; ++type_idx) {
fl::EaseType type = ALL_EASING_TYPES[type_idx].first;
const char* name = ALL_EASING_TYPES[type_idx].second;
// Test midpoint values - should be relatively close
uint8_t result8_mid = fl::ease8(type, 128);
uint16_t result16_mid = fl::ease16(type, 32768);
uint8_t scaled_result16_mid = map16_to_8(result16_mid);
int16_t diff = std::abs((int16_t)result8_mid - (int16_t)scaled_result16_mid);
INFO("Testing EaseType " << name << " midpoint consistency"
<< " (8-bit: " << (int)result8_mid
<< ", 16-bit scaled: " << (int)scaled_result16_mid
<< ", diff: " << diff << ")");
// Use the same tolerance as the general test
CHECK_LE(diff, tolerances[type_idx]);
}
}
}