sTodo-m5paper-client/libraries/FastLED/tests/test_line_simplification.cpp
2025-06-30 20:47:33 +02:00

207 lines
6.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// g++ --std=c++11 test.cpp
#include "test.h"
#include <random>
#include "fl/line_simplification.h"
#include "fl/warn.h"
using namespace fl;
TEST_CASE("Test Line Simplification") {
// defaultconstructed bitset is empty
LineSimplifier<float> ls;
ls.setMinimumDistance(0.1f);
fl::vector<vec2<float>> points;
points.push_back({0.0f, 0.0f});
points.push_back({1.0f, 1.0f});
points.push_back({2.0f, 2.0f});
points.push_back({3.0f, 3.0f});
points.push_back({4.0f, 4.0f});
ls.simplifyInplace(&points);
REQUIRE_EQ(2,
points.size()); // Only 2 points on co-linear line should remain.
REQUIRE_EQ(vec2<float>(0.0f, 0.0f), points[0]);
REQUIRE_EQ(vec2<float>(4.0f, 4.0f), points[1]);
}
TEST_CASE("Test simple triangle") {
LineSimplifier<float> ls;
fl::vector<vec2<float>> points;
points.push_back({0.0f, 0.0f}); // First point of triangle
points.push_back({0.5f, 0.5f});
points.push_back({0.0f, 1.0f});
float exceeds_thresh = 0.49f;
float under_thresh = 0.51f;
ls.setMinimumDistance(exceeds_thresh);
fl::vector<vec2<float>> output;
ls.simplify(points, &output);
REQUIRE_EQ(3, output.size());
REQUIRE_EQ(vec2<float>(0.0f, 0.0f), output[0]);
REQUIRE_EQ(vec2<float>(0.5f, 0.5f), output[1]);
REQUIRE_EQ(vec2<float>(0.0f, 1.0f), output[2]);
ls.setMinimumDistance(under_thresh);
ls.simplify(points, &output);
REQUIRE_EQ(2, output.size());
REQUIRE_EQ(vec2<float>(0.0f, 0.0f), output[0]);
REQUIRE_EQ(vec2<float>(0.0f, 1.0f), output[1]);
}
TEST_CASE("Test Line Simplification with Different Distance Thresholds") {
LineSimplifier<float> ls;
// Test with a triangle shape - non-collinear points
ls.setMinimumDistance(0.5f);
fl::vector<vec2<float>> points1;
points1.push_back({0.0f, 0.0f}); // First point of triangle
points1.push_back({0.3f, 0.3f}); // Should be filtered out (distance < 0.5)
points1.push_back({1.0f, 1.0f}); // Second point of triangle
points1.push_back({0.8f, 1.2f}); // Should be filtered out (distance < 0.5)
points1.push_back({0.0f, 2.0f}); // Third point of triangle
ls.simplifyInplace(&points1);
REQUIRE_EQ(3, points1.size()); // Triangle vertices should remain
REQUIRE_EQ(vec2<float>(0.0f, 0.0f), points1[0]);
REQUIRE_EQ(vec2<float>(1.0f, 1.0f), points1[1]);
REQUIRE_EQ(vec2<float>(0.0f, 2.0f), points1[2]);
}
TEST_CASE("Test Line Simplification with Complex Shape") {
// SUBCASE("at threshold") {
// LineSimplifier<float> ls;
// // Test with a more complex shape and smaller threshold
// ls.setMinimumDistance(0.1f);
// fl::vector<vec2<float>> points2;
// points2.push_back({0.0f, 0.0f}); // Start point
// points2.push_back({0.1f, 0.20f}); // Filtered out
// points2.push_back({0.0f, 0.29f}); // Filtered out
// points2.push_back({0.0f, 1.0f}); // Should be kept (distance > 0.2)
// ls.simplifyInplace(&points2);
// REQUIRE_EQ(3, points2.size());
// REQUIRE_EQ(vec2<float>(0.0f, 0.0f), points2[0]);
// REQUIRE_EQ(vec2<float>(0.10f, 0.10f), points2[1]);
// REQUIRE_EQ(vec2<float>(0.0f, 1.0f), points2[2]);
// };
SUBCASE("Above threshold") {
LineSimplifier<float> ls;
// Test with a more complex shape and larger threshold
ls.setMinimumDistance(0.101f);
fl::vector<vec2<float>> points3;
points3.push_back({0.0f, 0.0f}); // Start point
points3.push_back({0.1f, 0.1f}); // Filtered out
points3.push_back({0.0f, 0.3f}); // Filtered out
points3.push_back({0.0f, 1.0f}); // Should be kept (distance > 0.5)
ls.simplifyInplace(&points3);
REQUIRE_EQ(2, points3.size());
REQUIRE_EQ(vec2<float>(0.0f, 0.0f), points3[0]);
REQUIRE_EQ(vec2<float>(0.0f, 1.0f), points3[1]);
};
}
TEST_CASE("Iteratively find the closest point") {
LineSimplifier<float> ls;
fl::vector<vec2<float>> points;
points.push_back({0.0f, 0.0f}); // First point of triangle
points.push_back({0.5f, 0.5f});
points.push_back({0.0f, 1.0f});
float thresh = 0.0;
while (true) {
ls.setMinimumDistance(thresh);
fl::vector<vec2<float>> output;
ls.simplify(points, &output);
if (output.size() == 2) {
break;
}
thresh += 0.01f;
}
REQUIRE(ALMOST_EQUAL(thresh, 0.5f, 0.01f));
}
TEST_CASE("Binary search the the threshold that gives 3 points") {
LineSimplifierExact<float> ls;
fl::vector<vec2<float>> points;
points.push_back({0.0f, 0.0f}); // First point of triangle
points.push_back({0.5f, 0.5f});
points.push_back({0.0f, 1.0f});
points.push_back({0.6f, 2.0f});
points.push_back({0.0f, 6.0f});
ls.setCount(3);
fl::vector<vec2<float>> out;
ls.simplify(points, &out);
REQUIRE_EQ(3, out.size());
MESSAGE("Done");
}
TEST_CASE("Known bad") {
fl::vector<vec2<float>> points;
points.push_back({-3136.439941f, 2546.339844f});
points.push_back({4580.994141f, -3516.982422f});
points.push_back({-1228.554688f, -5104.814453f});
points.push_back({-8806.442383f, 3895.103516f});
points.push_back({-2039.114746f, 1878.047852f});
LineSimplifierExact<float> ls;
ls.setCount(3);
fl::vector<vec2<float>> out;
ls.simplify(points, &out);
MESSAGE("Output points: " << out.size());
MESSAGE("Output points: " << out);
REQUIRE_EQ(3, out.size());
}
// TEST_CASE("Binary search reduction to 3 points from 5 random points (1000 runs)") {
// constexpr int kTrials = 1000;
// constexpr int kInputPoints = 5;
// constexpr int kTargetPoints = 3;
// std::mt19937 rng(123); // fixed seed for reproducibility
// std::uniform_real_distribution<float> dist(-10000.0f, 10000.0f);
// for (int trial = 0; trial < kTrials; ++trial) {
// LineSimplifierExact<float> ls;
// fl::vector<vec2<float>> points;
// for (int i = 0; i < kInputPoints; ++i) {
// points.push_back({dist(rng), dist(rng)});
// }
// ls.setCount(kTargetPoints);
// fl::vector<vec2<float>> out;
// ls.simplify(points, &out);
// const bool bad_value = (out.size() != kTargetPoints);
// if (bad_value) {
// INFO("Trial " << trial << ": Input points: " << points.size()
// << ", Output points: " << out.size() << ", " << out);
// for (size_t i = 0; i < points.size(); ++i) {
// auto p = points[i];
// FASTLED_WARN("Input point " << i << ": " << p);
// }
// // Assert
// REQUIRE_EQ(kTargetPoints, out.size());
// }
// }
// MESSAGE("Completed 1000 trials of random 5→3 simplification");
// }