Files

324 lines
10 KiB
C++

// g++ --std=c++11 test.cpp
#include "test.h"
#include "test.h"
#include "lib8tion/intmap.h"
#include "fl/xypath.h"
#include "fl/vector.h"
#include "fl/unused.h"
#include <string>
using namespace fl;
#define MESSAGE_TILE(TILE) \
MESSAGE("\nTile:\n" \
<< " " << TILE.at(0, 0) << " " << TILE.at(1, 0) << "\n" \
<< " " << TILE.at(0, 1) << " " << TILE.at(1, 1) << "\n");
#define MESSAGE_TILE_ROW(TILE, ROW) \
MESSAGE("\nTile Row " << ROW << ":\n" \
<< " " << TILE.at(0, ROW) << " " << TILE.at(1, ROW) << "\n");
TEST_CASE("LinePath") {
LinePath path(0.0f, 0.0f, 1.0f, 1.0f);
vec2f xy = path.compute(0.5f);
REQUIRE(xy.x == 0.5f);
REQUIRE(xy.y == 0.5f);
xy = path.compute(1.0f);
REQUIRE(xy.x == 1.0f);
REQUIRE(xy.y == 1.0f);
xy = path.compute(0.0f);
REQUIRE(xy.x == 0.0f);
REQUIRE(xy.y == 0.0f);
}
TEST_CASE("LinePath at_subpixel") {
// Tests that we can get the correct subpixel values at center point 0,0
auto line = fl::make_shared<LinePath>(-1.0f, -1.0f, 1.0f, -1.0f);
XYPath path(line);
path.setDrawBounds(2,2);
Tile2x2_u8 tile = path.at_subpixel(0);
REQUIRE_EQ(vec2<uint16_t>(0, 0), tile.origin());
MESSAGE_TILE(tile);
REQUIRE_EQ(255, tile.at(0, 0));
}
TEST_CASE("LinePath simple float sweep") {
// Tests that we can get the correct gaussian values at center point 0,0
auto point = fl::make_shared<LinePath>(0, 1.f, 1.f, 1.f);
XYPath path(point);
auto xy = path.at(0);
//MESSAGE_TILE(tile);
REQUIRE_EQ(xy, vec2f(0.0f, 1.f));
xy = path.at(1);
REQUIRE_EQ(xy, vec2f(1.f, 1.f));
}
TEST_CASE("Point at exactly the middle") {
// Tests that we can get the correct gaussian values at center point 0,0
auto point = fl::make_shared<PointPath>(0.0, 0.0); // Right in middle.
XYPath path(point);
path.setDrawBounds(2,2);
// auto xy = path.at(0);
fl::Tile2x2_u8 sp = path.at_subpixel(0);
//MESSAGE_TILE(tile);
// REQUIRE_EQ(vec2f(0.0f, 0.f), sp);
// print out
auto origin = sp.origin();
MESSAGE("Origin: " << origin.x << ", " << origin.y);
MESSAGE(sp.at(0, 0));
MESSAGE(sp.at(0, 1));
MESSAGE(sp.at(1, 0));
MESSAGE(sp.at(1, 1));
// require that all alpha be the same
REQUIRE_EQ(sp.at(0, 0), sp.at(0, 1));
REQUIRE_EQ(sp.at(0, 0), sp.at(1, 0));
REQUIRE_EQ(sp.at(0, 0), sp.at(1, 1));
REQUIRE_EQ(sp.at(0, 0), 64);
}
TEST_CASE("LinePath simple sweep in draw bounds") {
// Tests that we can get the correct gaussian values at center point 0,0
auto point = fl::make_shared<LinePath>(-1.f, -1.f, 1.f, -1.f);
XYPath path(point);
int width = 2;
path.setDrawBounds(width, width);
auto begin = path.at(0);
auto end = path.at(1);
REQUIRE_EQ(vec2f(0.5f, 0.5f), begin);
REQUIRE_EQ(vec2f(1.5f, 0.5f), end);
}
TEST_CASE("LinePath at_subpixel moves x") {
// Tests that we can get the correct subpixel.
auto point = fl::make_shared<LinePath>(-1.f, -1.f, 1.f, -1.f);
XYPath path(point);
path.setDrawBounds(3, 3);
Tile2x2_u8 tile = path.at_subpixel(0.0f);
// MESSAGE_TILE(tile);
REQUIRE_EQ(tile.origin(), vec2<uint16_t>(0, 0));
REQUIRE_EQ(tile.at(0, 0), 255);
tile = path.at_subpixel(1.0f);
REQUIRE_EQ(tile.origin(), vec2<uint16_t>(2, 0));
REQUIRE_EQ(tile.at(0, 0), 255);
}
TEST_CASE("Test HeartPath") {
HeartPathPtr heart = fl::make_shared<HeartPath>();
// Track min and max values to help with scaling
float min_x = 1.0f;
float max_x = -1.0f;
float min_y = 1.0f;
float max_y = -1.0f;
// Sample points along the heart curve
const int num_samples = 100;
for (int i = 0; i < num_samples; i++) {
float alpha = static_cast<float>(i) / (num_samples - 1);
vec2f point = heart->compute(alpha);
// Update min/max values
min_x = MIN(min_x, point.x);
max_x = MAX(max_x, point.x);
min_y = MIN(min_y, point.y);
max_y = MAX(max_y, point.y);
// Print every 10th point for visual inspection
if (i % 10 == 0) {
MESSAGE("Heart point at alpha=" << alpha << ": (" << point.x << ", " << point.y << ")");
}
}
// Print the min/max values
MESSAGE("\nHeart shape bounds:");
MESSAGE("X range: [" << min_x << ", " << max_x << "]");
MESSAGE("Y range: [" << min_y << ", " << max_y << "]");
// Verify the heart is within the expected bounds
REQUIRE(min_x >= -1.0f);
REQUIRE(max_x <= 1.0f);
REQUIRE(min_y >= -1.0f);
REQUIRE(max_y <= 1.0f);
}
TEST_CASE("Test ArchimedeanSpiralPath") {
ArchimedeanSpiralPathPtr spiral = fl::make_shared<ArchimedeanSpiralPath>(3, 1.0f);
// Track min and max values to help with scaling
float min_x = 1.0f;
float max_x = -1.0f;
float min_y = 1.0f;
float max_y = -1.0f;
// Sample points along the spiral curve
const int num_samples = 100;
for (int i = 0; i < num_samples; i++) {
float alpha = static_cast<float>(i) / (num_samples - 1);
vec2f point = spiral->compute(alpha);
// Update min/max values
min_x = MIN(min_x, point.x);
max_x = MAX(max_x, point.x);
min_y = MIN(min_y, point.y);
max_y = MAX(max_y, point.y);
// Print every 10th point for visual inspection
if (i % 10 == 0) {
MESSAGE("Spiral point at alpha=" << alpha << ": (" << point.x << ", " << point.y << ")");
}
}
// Print the min/max values
MESSAGE("\nSpiral shape bounds:");
MESSAGE("X range: [" << min_x << ", " << max_x << "]");
MESSAGE("Y range: [" << min_y << ", " << max_y << "]");
// Verify the spiral is within the expected bounds
REQUIRE(min_x >= -1.0f);
REQUIRE(max_x <= 1.0f);
REQUIRE(min_y >= -1.0f);
REQUIRE(max_y <= 1.0f);
}
TEST_CASE("Test RosePath") {
// Test with different petal configurations
SUBCASE("3-petal rose") {
RosePathPtr rose = fl::make_shared<RosePath>(3, 1);
// Track min and max values to help with scaling
float min_x = 1.0f;
float max_x = -1.0f;
float min_y = 1.0f;
float max_y = -1.0f;
// Sample points along the rose curve
const int num_samples = 100;
for (int i = 0; i < num_samples; i++) {
float alpha = static_cast<float>(i) / (num_samples - 1);
vec2f point = rose->compute(alpha);
// Update min/max values
min_x = MIN(min_x, point.x);
max_x = MAX(max_x, point.x);
min_y = MIN(min_y, point.y);
max_y = MAX(max_y, point.y);
// Print every 10th point for visual inspection
if (i % 10 == 0) {
MESSAGE("3-petal rose point at alpha=" << alpha << ": (" << point.x << ", " << point.y << ")");
}
}
// Print the min/max values
MESSAGE("\n3-petal rose shape bounds:");
MESSAGE("X range: [" << min_x << ", " << max_x << "]");
MESSAGE("Y range: [" << min_y << ", " << max_y << "]");
// Verify the rose is within the expected bounds
REQUIRE(min_x >= -1.0f);
REQUIRE(max_x <= 1.0f);
REQUIRE(min_y >= -1.0f);
REQUIRE(max_y <= 1.0f);
}
SUBCASE("4-petal rose") {
RosePathPtr rose = fl::make_shared<RosePath>(2, 1); // n=2 gives 4 petals
// Track min and max values to help with scaling
float min_x = 1.0f;
float max_x = -1.0f;
float min_y = 1.0f;
float max_y = -1.0f;
// Sample points along the rose curve
const int num_samples = 100;
for (int i = 0; i < num_samples; i++) {
float alpha = static_cast<float>(i) / (num_samples - 1);
vec2f point = rose->compute(alpha);
// Update min/max values
min_x = MIN(min_x, point.x);
max_x = MAX(max_x, point.x);
min_y = MIN(min_y, point.y);
max_y = MAX(max_y, point.y);
}
// Verify the rose is within the expected bounds
REQUIRE(min_x >= -1.0f);
REQUIRE(max_x <= 1.0f);
REQUIRE(min_y >= -1.0f);
REQUIRE(max_y <= 1.0f);
}
}
TEST_CASE("Check complex types") {
HeapVector<XYPathPtr> paths;
XYPathPtr circle = XYPath::NewCirclePath();
paths.push_back(circle);
// Add heart path to the tests
XYPathPtr heart = XYPath::NewHeartPath();
paths.push_back(heart);
// Add spiral path to the tests
XYPathPtr spiral = XYPath::NewArchimedeanSpiralPath();
paths.push_back(spiral);
// Add rose path to the tests
XYPathPtr rose = XYPath::NewRosePath();
paths.push_back(rose);
// Add phyllotaxis path to the tests
XYPathPtr phyllotaxis = XYPath::NewPhyllotaxisPath();
paths.push_back(phyllotaxis);
// paths.push_back(fl::make_intrusive<LissajousPath>());
// paths.push_back(fl::make_intrusive<GielisCurvePath>());
// paths.push_back(fl::make_intrusive<CatmullRomPath>());
SUBCASE("Check floating point range") {
for (auto &path : paths) {
for (float alpha = 0.0f; true; alpha += 0.01f) {
alpha = MIN(1.f, alpha);
vec2f xy = path->at(alpha);
REQUIRE(xy.x >= -1.0f);
REQUIRE(xy.x <= 1.0f);
REQUIRE(xy.y >= -1.0f);
REQUIRE(xy.y <= 1.0f);
if (ALMOST_EQUAL(alpha, 1.0f, 0.001f)) {
break;
}
}
}
}
SUBCASE("Check float point range with transform to -8,8") {
TransformFloat tx;
tx.set_scale(4.0f);
for (auto &path : paths) {
for (float alpha = 0.0f; true; alpha += 0.01f) {
alpha = MIN(1.f, alpha);
vec2f xy = path->at(alpha, tx);
REQUIRE_GE(xy.x, -4.0f);
REQUIRE_LE(xy.x, 4.0f);
REQUIRE_GE(xy.y, -4.0f);
REQUIRE_LE(xy.y, 4.0f);
if (ALMOST_EQUAL(alpha, 1.0f, 0.001f)) {
break;
}
}
}
}
}