3#define DOCTEST_CONFIG_IMPLEMENT
6#include "doctest_utils.h"
10TEST_CASE(
"Visualizer::disableMessages") {
11 Visualizer visualizer(1000, 800, 16,
false,
true);
13 DOCTEST_CHECK_NOTHROW(visualizer.disableMessages());
16 visualizer.setColorbarRange(20, 10);
21TEST_CASE(
"Visualizer::enableMessages") {
22 Visualizer visualizer(1000, 800, 16,
true,
true);
24 DOCTEST_CHECK_NOTHROW(visualizer.enableMessages());
27 visualizer.setColorbarRange(20, 10);
32TEST_CASE(
"Visualizer::setCameraPosition") {
33 Visualizer visualizer(1000, 800, 16,
true,
true);
36 visualizer.setCameraPosition(initial_position, initial_lookat);
39 std::vector<helios::vec3> positions = visualizer.getCameraPosition();
40 DOCTEST_CHECK(positions.size() == 2);
41 DOCTEST_CHECK(positions.at(1) == initial_position);
42 DOCTEST_CHECK(positions.at(0) == initial_lookat);
45TEST_CASE(
"Visualizer::setLightingModel") {
46 Visualizer visualizer(1000, 800, 16,
true,
true);
53TEST_CASE(
"Visualizer::setBackgroundColor and Visualizer::getBackgroundColor") {
54 Visualizer visualizer(1000, 800, 16,
true,
true);
56 visualizer.setBackgroundColor(bgcolor);
57 DOCTEST_CHECK(visualizer.getBackgroundColor() == bgcolor);
60TEST_CASE(
"Visualizer::setLightIntensityFactor") {
61 Visualizer visualizer(1000, 800, 16,
true,
true);
62 DOCTEST_CHECK_NOTHROW(visualizer.setLightIntensityFactor(0.75f));
65TEST_CASE(
"Visualizer::enableColorbar and Visualizer::disableColorbar") {
66 Visualizer visualizer(1000, 800, 16,
true,
true);
67 DOCTEST_CHECK_NOTHROW(visualizer.enableColorbar());
68 DOCTEST_CHECK_NOTHROW(visualizer.disableColorbar());
71TEST_CASE(
"Visualizer::setColorbarPosition") {
72 Visualizer visualizer(1000, 800, 16,
true,
true);
73 DOCTEST_CHECK_NOTHROW(visualizer.setColorbarPosition(
make_vec3(0.5f, 0.5f, 0.f)));
75 DOCTEST_CHECK_THROWS_AS(visualizer.setColorbarPosition(
make_vec3(-0.1f, 0.f, 0.f)), std::runtime_error);
78TEST_CASE(
"Visualizer::setColorbarSize") {
79 Visualizer visualizer(1000, 800, 16,
true,
true);
80 DOCTEST_CHECK_NOTHROW(visualizer.setColorbarSize(
make_vec2(0.1f, 0.05f)));
82 DOCTEST_CHECK_THROWS_AS(visualizer.setColorbarSize(
make_vec2(1.5f, 0.f)), std::runtime_error);
85TEST_CASE(
"Visualizer::setColorbarRange") {
86 Visualizer visualizer(1000, 800, 16,
true,
true);
87 visualizer.enableMessages();
88 visualizer.setColorbarRange(0.f, 1.f);
90 DOCTEST_CHECK_NOTHROW(visualizer.setColorbarRange(20.f, 10.f));
94TEST_CASE(
"Visualizer::setColorbarTicks") {
95 Visualizer visualizer(1000, 800, 16,
true,
true);
96 visualizer.setColorbarRange(0.f, 1.f);
97 std::vector<float> ticks{0.f, 0.5f, 1.f};
98 DOCTEST_CHECK_NOTHROW(visualizer.setColorbarTicks(ticks));
100 DOCTEST_CHECK_THROWS_AS(visualizer.setColorbarTicks({}), std::runtime_error);
101 DOCTEST_CHECK_THROWS_AS(visualizer.setColorbarTicks({0.f, 0.5f, 0.4f}), std::runtime_error);
104TEST_CASE(
"Visualizer::generateNiceTicks - Float data") {
106 std::vector<float> ticks;
110 DOCTEST_CHECK(ticks.size() >= 2);
111 DOCTEST_CHECK(ticks.front() <= 0.0f);
112 DOCTEST_CHECK(ticks.back() >= 1.0f);
114 for (
size_t i = 1; i < ticks.size(); ++i) {
115 DOCTEST_CHECK(ticks[i] > ticks[i - 1]);
120 DOCTEST_CHECK(ticks.size() >= 2);
121 DOCTEST_CHECK(ticks.front() <= 0.0f);
122 DOCTEST_CHECK(ticks.back() >= 100.0f);
126 DOCTEST_CHECK(ticks.size() >= 2);
127 DOCTEST_CHECK(ticks.front() <= 0.0f);
128 DOCTEST_CHECK(ticks.back() >= 48.3f);
133 DOCTEST_CHECK(ticks.size() >= 2);
137 DOCTEST_CHECK(ticks.size() >= 2);
138 DOCTEST_CHECK(ticks.front() <= -10.0f);
139 DOCTEST_CHECK(ticks.back() >= 10.0f);
143 DOCTEST_CHECK(ticks.size() >= 2);
146TEST_CASE(
"Visualizer::generateNiceTicks - Integer data") {
147 std::vector<float> ticks;
151 DOCTEST_CHECK(ticks.size() >= 2);
153 for (
float tick: ticks) {
154 DOCTEST_CHECK(std::fabs(tick - std::round(tick)) < 1e-6);
159 DOCTEST_CHECK(ticks.size() >= 2);
160 for (
float tick: ticks) {
161 DOCTEST_CHECK(std::fabs(tick - std::round(tick)) < 1e-6);
166 DOCTEST_CHECK(ticks.size() >= 2);
167 for (
float tick: ticks) {
168 DOCTEST_CHECK(std::fabs(tick - std::round(tick)) < 1e-6);
172TEST_CASE(
"Visualizer::formatTickLabel - Float data") {
177 DOCTEST_CHECK(label ==
"0.0");
180 DOCTEST_CHECK(label ==
"0.4");
183 DOCTEST_CHECK(label ==
"1.0");
187 DOCTEST_CHECK(label ==
"0");
190 DOCTEST_CHECK(label ==
"10");
194 DOCTEST_CHECK(label ==
"0.5");
198 DOCTEST_CHECK(label.find(
"e") != std::string::npos);
202 DOCTEST_CHECK(label.find(
"e") != std::string::npos);
206 DOCTEST_CHECK(label.find(
"e") == std::string::npos);
209TEST_CASE(
"Visualizer::formatTickLabel - Integer data") {
214 DOCTEST_CHECK(label ==
"0");
217 DOCTEST_CHECK(label ==
"5");
220 DOCTEST_CHECK(label ==
"100");
224 DOCTEST_CHECK(label ==
"5");
227 DOCTEST_CHECK(label ==
"6");
231 DOCTEST_CHECK(label.find(
"e") != std::string::npos);
235 DOCTEST_CHECK(label ==
"9000");
238TEST_CASE(
"Visualizer::niceNumber") {
265TEST_CASE(
"Visualizer colorbar text attributes") {
266 Visualizer visualizer(1000, 800, 16,
true,
true);
267 DOCTEST_CHECK_NOTHROW(visualizer.setColorbarTitle(
"MyBar"));
268 DOCTEST_CHECK_NOTHROW(visualizer.setColorbarFontColor(RGB::yellow));
269 DOCTEST_CHECK_NOTHROW(visualizer.setColorbarFontSize(14));
271 DOCTEST_CHECK_THROWS_AS(visualizer.setColorbarFontSize(0), std::runtime_error);
274TEST_CASE(
"Visualizer::setColormap") {
275 Visualizer visualizer(1000, 800, 16,
true,
true);
279 DOCTEST_CHECK_THROWS_AS(visualizer.setColormap(std::vector<RGBcolor>{RGB::red}, std::vector<float>{0.f, 1.f}), std::runtime_error);
282TEST_CASE(
"Visualizer::PNG texture integration via primitives") {
283 Visualizer visualizer(1000, 800, 16,
true,
true);
284 const char *png_filename =
"plugins/visualizer/textures/AlmondLeaf.png";
287 DOCTEST_CHECK(std::filesystem::exists(png_filename));
290 std::vector<helios::vec3> verts = {
make_vec3(0, 0, 0),
make_vec3(1, 0, 0),
make_vec3(1, 1, 0),
make_vec3(0, 1, 0)};
293 DOCTEST_CHECK(UUID1 != 0);
297 DOCTEST_CHECK_NOTHROW(UUID2 = visualizer.addTriangle(
make_vec3(0, 0, 0),
make_vec3(1, 0, 0),
make_vec3(0, 1, 0), png_filename,
make_vec2(0, 0),
make_vec2(1, 0),
make_vec2(0, 1),
Visualizer::COORDINATES_CARTESIAN));
298 DOCTEST_CHECK(UUID2 != 0);
299 DOCTEST_CHECK(UUID2 != UUID1);
302TEST_CASE(
"Visualizer::JPEG texture integration via primitives") {
303 Visualizer visualizer(1000, 800, 16,
true,
true);
304 const char *jpeg_filename =
"plugins/visualizer/textures/SkyDome_clouds.jpg";
307 DOCTEST_CHECK(std::filesystem::exists(jpeg_filename));
310 std::vector<helios::vec3> verts = {
make_vec3(1, 1, 1),
make_vec3(3, 1, 1),
make_vec3(3, 3, 1),
make_vec3(1, 3, 1)};
313 DOCTEST_CHECK(rect_UUID != 0);
316TEST_CASE(
"Visualizer::Visualizer") {
317 DOCTEST_CHECK_NOTHROW(
Visualizer v1(800, 600,
true));
318 DOCTEST_CHECK_NOTHROW(
Visualizer v2(1024, 768, 4,
false,
true));
319 DOCTEST_CHECK_NOTHROW(
Visualizer v3(1280, 720, 8,
false,
true));
322TEST_CASE(
"Visualizer texture copy") {
323 DOCTEST_CHECK(std::filesystem::exists(
"plugins/visualizer/textures/AlmondLeaf.png"));
324 DOCTEST_CHECK(std::filesystem::exists(
"plugins/visualizer/textures/Helios_watermark.png"));
325 DOCTEST_CHECK(std::filesystem::exists(
"plugins/visualizer/textures/SkyDome_clouds.jpg"));
328TEST_CASE(
"Visualizer::addRectangleByCenter") {
329 Visualizer visualizer(1000, 800, 16,
true,
true);
332 DOCTEST_CHECK(UUID != 0);
335TEST_CASE(
"Visualizer::addRectangleByCenter extreme") {
336 Visualizer visualizer(1000, 800, 16,
true,
true);
339 DOCTEST_CHECK(UUID != 0);
342TEST_CASE(
"Visualizer::addRectangleByVertices variations") {
343 Visualizer visualizer(1000, 800, 16,
true,
true);
344 std::vector<helios::vec3> verts = {
make_vec3(0, 0, 0),
make_vec3(1, 0, 0),
make_vec3(1, 1, 0),
make_vec3(0, 1, 0)};
347 DOCTEST_CHECK(UUID1 != 0);
350 DOCTEST_CHECK(UUID2 != 0);
353TEST_CASE(
"Visualizer::addTriangle") {
354 Visualizer visualizer(1000, 800, 16,
true,
true);
356 DOCTEST_CHECK_NOTHROW(UUID = visualizer.addTriangle(
make_vec3(0, 0, 0),
make_vec3(1, 0, 0),
make_vec3(0, 1, 0), RGB::blue,
Visualizer::COORDINATES_CARTESIAN));
357 DOCTEST_CHECK(UUID != 0);
360TEST_CASE(
"Visualizer::addTriangle textured") {
361 Visualizer visualizer(1000, 800, 16,
true,
true);
363 DOCTEST_CHECK_NOTHROW(UUID = visualizer.addTriangle(
make_vec3(0, 0, 0),
make_vec3(1, 0, 0),
make_vec3(0, 1, 0),
"plugins/visualizer/textures/AlmondLeaf.png",
make_vec2(0, 0),
make_vec2(1, 0),
make_vec2(0, 1),
Visualizer::COORDINATES_CARTESIAN));
364 DOCTEST_CHECK(UUID != 0);
367TEST_CASE(
"Visualizer::addVoxelByCenter") {
368 Visualizer visualizer(1000, 800, 16,
true,
true);
369 std::vector<size_t> UUIDs;
371 DOCTEST_CHECK(UUIDs.size() == 6);
374TEST_CASE(
"Visualizer::addSphereByCenter") {
375 Visualizer visualizer(1000, 800, 16,
true,
true);
377 std::vector<size_t> UUIDs;
379 DOCTEST_CHECK(UUIDs.size() == 2 * N * (N - 1));
382TEST_CASE(
"Visualizer::addCoordinateAxes") {
383 Visualizer visualizer(1000, 800, 16,
true,
true);
384 DOCTEST_CHECK_NOTHROW(visualizer.addCoordinateAxes(
make_vec3(0, 0, 0),
make_vec3(1, 1, 1),
"XYZ"));
387TEST_CASE(
"Visualizer::addLine") {
388 Visualizer visualizer(1000, 800, 16,
true,
true);
392TEST_CASE(
"Visualizer::addLine with line width") {
393 Visualizer visualizer(1000, 800, 16,
true,
true);
398 DOCTEST_CHECK(UUID1 != 0);
402 DOCTEST_CHECK_NOTHROW(UUID2 = visualizer.addLine(
make_vec3(2, 0, 0),
make_vec3(3, 1, 1),
make_RGBAcolor(1.0f, 0.0f, 0.0f, 0.5f), 5.0f,
Visualizer::COORDINATES_CARTESIAN));
403 DOCTEST_CHECK(UUID2 != 0);
404 DOCTEST_CHECK(UUID2 != UUID1);
409 DOCTEST_CHECK(UUID3 != 0);
414 DOCTEST_CHECK(UUID4 != 0);
419 DOCTEST_CHECK(UUID5 != 0);
433 DOCTEST_CHECK(UUID6 != 0);
436TEST_CASE(
"Visualizer::validateTextureFile") {
439 DOCTEST_CHECK(!
validateTextureFile(
"plugins/visualizer/textures/SkyDome_clouds.jpg",
true));
442TEST_CASE(
"Visualizer::point culling configuration simple") {
446 DOCTEST_CHECK_NOTHROW(visualizer.setPointCullingEnabled(
true));
447 DOCTEST_CHECK_NOTHROW(visualizer.setPointCullingEnabled(
false));
450TEST_CASE(
"Visualizer::addPoint basic functionality") {
455 DOCTEST_CHECK(point_uuid != 0);
458TEST_CASE(
"Visualizer::addPoint with different sizes") {
472 DOCTEST_CHECK(point1 != 0);
473 DOCTEST_CHECK(point2 != 0);
474 DOCTEST_CHECK(point3 != 0);
475 DOCTEST_CHECK(point4 != 0);
476 DOCTEST_CHECK(point1 != point2);
477 DOCTEST_CHECK(point2 != point3);
478 DOCTEST_CHECK(point3 != point4);
481TEST_CASE(
"Visualizer::addPoint RGBA with sizes") {
488 DOCTEST_CHECK(point1 != 0);
489 DOCTEST_CHECK(point2 != 0);
490 DOCTEST_CHECK(point1 != point2);
493TEST_CASE(
"Visualizer::point culling metrics functionality") {
497 size_t total, rendered;
499 DOCTEST_CHECK_NOTHROW(visualizer.getPointRenderingMetrics(total, rendered, time));
502 for (
int i = 0; i < 5; ++i) {
504 DOCTEST_CHECK(uuid != 0);
508 DOCTEST_CHECK_NOTHROW(visualizer.getPointRenderingMetrics(total, rendered, time));
513TEST_CASE(
"Visualizer::point size edge cases") {
519 DOCTEST_CHECK(point1 != 0);
525 DOCTEST_CHECK(point2 != 0);
531 DOCTEST_CHECK(point3 != 0);
535 DOCTEST_CHECK(point1 != point2);
536 DOCTEST_CHECK(point2 != point3);
537 DOCTEST_CHECK(point1 != point3);
540TEST_CASE(
"CI/Offscreen - Basic OpenGL Context") {
542 DOCTEST_CHECK_NOTHROW({
543 Visualizer visualizer(400, 300, 4,
true,
true);
548TEST_CASE(
"CI/Offscreen - Framebuffer Operations") {
549 Visualizer visualizer(200, 150, 0,
true,
true);
552 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundColor(RGB::black));
553 DOCTEST_CHECK_NOTHROW(visualizer.setLightDirection(
make_vec3(0, 0, -1)));
554 DOCTEST_CHECK_NOTHROW(visualizer.setLightIntensityFactor(1.0f));
557TEST_CASE(
"CI/Offscreen - Geometry Rendering") {
558 Visualizer visualizer(100, 100, 0,
true,
true);
561 size_t triangle = visualizer.addTriangle(
make_vec3(0, 0, 0),
make_vec3(1, 0, 0),
make_vec3(0.5, 1, 0), RGB::red,
Visualizer::COORDINATES_CARTESIAN);
562 DOCTEST_CHECK(triangle != 0);
565 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundColor(RGB::black));
568TEST_CASE(
"CI/Offscreen - Environment Variable Detection") {
573 DOCTEST_CHECK_NOTHROW({
574 Visualizer visualizer(100, 100, 0,
true,
false);
579TEST_CASE(
"CI/Offscreen - Render Target Switching") {
583 DOCTEST_CHECK_NOTHROW(visualizer.renderToOffscreenBuffer());
586 size_t triangle = visualizer.addTriangle(
make_vec3(0, 0, 0),
make_vec3(1, 0, 0),
make_vec3(0.5, 1, 0),
make_RGBcolor(1, 1, 1),
Visualizer::COORDINATES_CARTESIAN);
587 DOCTEST_CHECK(triangle != 0);
590 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundColor(RGB::black));
593TEST_CASE(
"CI/Offscreen - Stress Test") {
595 std::vector<std::unique_ptr<Visualizer>> visualizers;
597 for (
int i = 0; i < 3; ++i) {
598 DOCTEST_CHECK_NOTHROW({ visualizers.emplace_back(std::make_unique<Visualizer>(32, 32, 0,
true,
true)); });
602 for (
const auto &vis: visualizers) {
603 DOCTEST_CHECK(vis !=
nullptr);
609TEST_CASE(
"Visualizer::printWindow after plotUpdate regression test") {
616 Visualizer visualizer(200, 200, 0,
true,
true);
617 visualizer.disableMessages();
622 std::string test_material =
"test_visualizer_red_sphere";
630 visualizer.buildContextGeometry(&context);
636 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
639 std::string test_filename =
"test_printWindow_regression.jpg";
640 DOCTEST_CHECK_NOTHROW(visualizer.printWindow(test_filename.c_str()));
643 DOCTEST_CHECK(std::filesystem::exists(test_filename));
647 std::vector<uint> pixel_buffer(200 * 200 * 3);
648 DOCTEST_CHECK_NOTHROW(visualizer.getWindowPixelsRGB(pixel_buffer.data()));
651 bool has_non_black_pixels =
false;
652 for (
size_t i = 0; i < pixel_buffer.size(); i++) {
653 if (pixel_buffer[i] > 10) {
654 has_non_black_pixels =
true;
659 DOCTEST_CHECK_MESSAGE(has_non_black_pixels,
"Image appears to be all black - this indicates the original buffer reading issue");
668 if (std::filesystem::exists(test_filename)) {
669 std::filesystem::remove(test_filename);
673TEST_CASE(
"Visualizer::printWindow after plotUpdate non-headless regression test") {
678 const char *display = std::getenv(
"DISPLAY");
679 const char *wayland_display = std::getenv(
"WAYLAND_DISPLAY");
683 bool has_display =
true;
686 bool has_display = (display !=
nullptr && strlen(display) > 0) || (wayland_display !=
nullptr && strlen(wayland_display) > 0);
695 Visualizer visualizer(200, 200, 0,
true,
false);
696 visualizer.disableMessages();
701 std::string test_material =
"test_visualizer_red_sphere";
709 visualizer.buildContextGeometry(&context);
715 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
718 std::string test_filename =
"test_printWindow_nonheadless_regression.jpg";
719 DOCTEST_CHECK_NOTHROW(visualizer.printWindow(test_filename.c_str()));
722 DOCTEST_CHECK(std::filesystem::exists(test_filename));
726 std::vector<uint> pixel_buffer(200 * 200 * 3);
727 DOCTEST_CHECK_NOTHROW(visualizer.getWindowPixelsRGB(pixel_buffer.data()));
730 bool has_non_black_pixels =
false;
731 for (
size_t i = 0; i < pixel_buffer.size(); i++) {
732 if (pixel_buffer[i] > 10) {
733 has_non_black_pixels =
true;
738 DOCTEST_CHECK_MESSAGE(has_non_black_pixels,
"Image appears to be all black in non-headless mode - buffer reading issue");
741 if (std::filesystem::exists(test_filename)) {
742 std::filesystem::remove(test_filename);
746TEST_CASE(
"Visualizer::PNG with transparent background") {
748 Visualizer visualizer(200, 200, 16,
true,
true);
749 visualizer.disableMessages();
752 std::vector<helios::vec3> vertices{
make_vec3(-0.3f, -0.3f, 0.f),
make_vec3(0.3f, -0.3f, 0.f),
make_vec3(0.3f, 0.3f, 0.f),
make_vec3(-0.3f, 0.3f, 0.f)};
758 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundTransparent());
761 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
764 std::string test_filename =
"test_transparent_bg.png";
765 DOCTEST_CHECK_NOTHROW(visualizer.printWindow(test_filename.c_str(),
"png"));
766 DOCTEST_CHECK(std::filesystem::exists(test_filename));
769 std::vector<helios::RGBAcolor> pixel_data;
771 DOCTEST_CHECK_NOTHROW(helios::readPNG(test_filename, width, height, pixel_data));
772 DOCTEST_CHECK(width == 200);
773 DOCTEST_CHECK(height == 200);
774 DOCTEST_CHECK(pixel_data.size() == width * height);
777 int transparent_pixels = 0;
778 int opaque_red_pixels = 0;
780 for (
const auto &pixel: pixel_data) {
781 if (pixel.a < 0.1f) {
783 transparent_pixels++;
784 }
else if (pixel.a > 0.9f && pixel.r > 0.5f && pixel.g < 0.3f && pixel.b < 0.3f) {
791 DOCTEST_CHECK_MESSAGE(transparent_pixels > 1000,
"Expected significant transparent background area, got " << transparent_pixels <<
" transparent pixels");
792 DOCTEST_CHECK_MESSAGE(opaque_red_pixels > 100,
"Expected visible red rectangle in center, got " << opaque_red_pixels <<
" red pixels");
795 DOCTEST_CHECK_MESSAGE(transparent_pixels + opaque_red_pixels > 0.8 * (width * height),
"Transparent + opaque pixels should account for most of image");
798 if (std::filesystem::exists(test_filename)) {
799 std::filesystem::remove(test_filename);
803TEST_CASE(
"Visualizer::PNG with transparent background (windowed mode)") {
810 std::string test_material =
"test_visualizer_red_patch";
814 context.
setMaterialTexture(test_material,
"plugins/visualizer/textures/AlmondLeaf.png");
819 Visualizer visualizer(200, 200, 16,
false,
true);
820 visualizer.disableMessages();
823 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundTransparent());
829 DOCTEST_CHECK_NOTHROW(visualizer.buildContextGeometry(&context));
832 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate());
835 std::string test_filename =
"test_transparent_bg_windowed.png";
836 DOCTEST_CHECK_NOTHROW(visualizer.printWindow(test_filename.c_str(),
"png"));
837 DOCTEST_CHECK(std::filesystem::exists(test_filename));
840 std::vector<helios::RGBAcolor> pixel_data;
842 DOCTEST_CHECK_NOTHROW(helios::readPNG(test_filename, width, height, pixel_data));
844 DOCTEST_CHECK(width > 0);
845 DOCTEST_CHECK(height > 0);
846 DOCTEST_CHECK(pixel_data.size() == width * height);
849 int transparent_pixels = 0;
850 int opaque_red_pixels = 0;
851 int checkerboard_pixels = 0;
853 for (
const auto &pixel: pixel_data) {
854 if (pixel.a < 0.1f) {
856 transparent_pixels++;
857 }
else if (pixel.a > 0.9f && pixel.r > 0.5f && pixel.g < 0.3f && pixel.b < 0.3f) {
860 }
else if (pixel.a > 0.9f && pixel.r > 0.6f && pixel.r < 0.85f && std::abs(pixel.r - pixel.g) < 0.1f && std::abs(pixel.r - pixel.b) < 0.1f) {
862 checkerboard_pixels++;
867 DOCTEST_CHECK_MESSAGE(checkerboard_pixels == 0,
"Checkerboard texture should not appear in PNG output, got " << checkerboard_pixels <<
" checkerboard pixels");
870 uint total_pixels = width * height;
871 DOCTEST_CHECK_MESSAGE(transparent_pixels > total_pixels * 0.25,
"Expected significant transparent background area, got " << transparent_pixels <<
" transparent pixels out of " << total_pixels);
874 DOCTEST_CHECK_MESSAGE(opaque_red_pixels > total_pixels * 0.025,
"Expected visible red rectangle in center, got " << opaque_red_pixels <<
" red pixels out of " << total_pixels);
877 if (std::filesystem::exists(test_filename)) {
878 std::filesystem::remove(test_filename);
882TEST_CASE(
"Visualizer::Transparent background with non-square window") {
891 std::string test_material =
"test_visualizer_red_patch_small";
895 context.
setMaterialTexture(test_material,
"plugins/visualizer/textures/AlmondLeaf.png");
901 Visualizer visualizer(800, 600, 16,
false,
true);
902 visualizer.disableMessages();
905 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundTransparent());
908 DOCTEST_CHECK_NOTHROW(visualizer.buildContextGeometry(&context));
911 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
913 std::string test_filename =
"test_transparent_bg_nonsquare.png";
914 DOCTEST_CHECK_NOTHROW(visualizer.printWindow(test_filename.c_str(),
"png"));
915 DOCTEST_CHECK(std::filesystem::exists(test_filename));
918 std::vector<helios::RGBAcolor> pixel_data;
920 DOCTEST_CHECK_NOTHROW(helios::readPNG(test_filename, width, height, pixel_data));
921 DOCTEST_CHECK(width > 0);
922 DOCTEST_CHECK(height > 0);
925 int transparent_pixels = 0;
926 int opaque_red_pixels = 0;
928 for (
const auto &pixel: pixel_data) {
929 if (pixel.a < 0.1f) {
930 transparent_pixels++;
931 }
else if (pixel.a > 0.9f && pixel.r > 0.5f && pixel.g < 0.3f && pixel.b < 0.3f) {
937 uint total_pixels = width * height;
938 DOCTEST_CHECK_MESSAGE(transparent_pixels > total_pixels * 0.5,
"Expected significant transparent background, got " << transparent_pixels <<
" transparent pixels out of " << total_pixels);
939 DOCTEST_CHECK_MESSAGE(opaque_red_pixels > 100,
"Expected visible red rectangle, got " << opaque_red_pixels <<
" red pixels");
942 if (std::filesystem::exists(test_filename)) {
943 std::filesystem::remove(test_filename);
947TEST_CASE(
"Visualizer::Background color/transparent switching") {
953 std::string test_material =
"test_visualizer_red_patch_small";
957 context.
setMaterialTexture(test_material,
"plugins/visualizer/textures/AlmondLeaf.png");
962 SUBCASE(
"Watermark visible → transparent → solid color (should restore watermark)") {
963 Visualizer visualizer(200, 200, 16,
false,
true);
964 visualizer.disableMessages();
965 visualizer.buildContextGeometry(&context);
971 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundTransparent());
974 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundColor(
make_RGBcolor(0.5f, 0.5f, 0.5f)));
977 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
980 std::string test_filename =
"test_bg_switch_restore.png";
981 DOCTEST_CHECK_NOTHROW(visualizer.printWindow(test_filename.c_str(),
"png"));
982 DOCTEST_CHECK(std::filesystem::exists(test_filename));
984 if (std::filesystem::exists(test_filename)) {
985 std::filesystem::remove(test_filename);
989 SUBCASE(
"Watermark hidden → transparent → solid color (should NOT restore watermark)") {
990 Visualizer visualizer(200, 200, 16,
false,
true);
991 visualizer.disableMessages();
992 visualizer.buildContextGeometry(&context);
995 DOCTEST_CHECK_NOTHROW(visualizer.hideWatermark());
998 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundTransparent());
1001 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundColor(
make_RGBcolor(0.5f, 0.5f, 0.5f)));
1004 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1006 std::string test_filename =
"test_bg_switch_no_restore.png";
1007 DOCTEST_CHECK_NOTHROW(visualizer.printWindow(test_filename.c_str(),
"png"));
1008 DOCTEST_CHECK(std::filesystem::exists(test_filename));
1010 if (std::filesystem::exists(test_filename)) {
1011 std::filesystem::remove(test_filename);
1015 SUBCASE(
"Multiple switches between transparent and solid") {
1016 Visualizer visualizer(200, 200, 16,
false,
true);
1017 visualizer.disableMessages();
1018 visualizer.buildContextGeometry(&context);
1021 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundTransparent());
1022 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundColor(
make_RGBcolor(1.f, 0.f, 0.f)));
1023 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundTransparent());
1024 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundColor(
make_RGBcolor(0.f, 1.f, 0.f)));
1025 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundTransparent());
1026 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundColor(
make_RGBcolor(0.f, 0.f, 1.f)));
1028 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1032DOCTEST_TEST_CASE(
"Visualizer::Navigation Gizmo") {
1035 SUBCASE(
"Navigation gizmo is enabled by default") {
1036 Visualizer visualizer(200, 200, 16,
false,
true);
1037 visualizer.disableMessages();
1040 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1043 SUBCASE(
"Show and hide navigation gizmo") {
1044 Visualizer visualizer(200, 200, 16,
false,
true);
1045 visualizer.disableMessages();
1048 DOCTEST_CHECK_NOTHROW(visualizer.hideNavigationGizmo());
1049 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1052 DOCTEST_CHECK_NOTHROW(visualizer.showNavigationGizmo());
1053 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1056 DOCTEST_CHECK_NOTHROW(visualizer.hideNavigationGizmo());
1057 DOCTEST_CHECK_NOTHROW(visualizer.showNavigationGizmo());
1058 DOCTEST_CHECK_NOTHROW(visualizer.hideNavigationGizmo());
1059 DOCTEST_CHECK_NOTHROW(visualizer.showNavigationGizmo());
1060 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1063 SUBCASE(
"Navigation gizmo with camera movement") {
1064 Visualizer visualizer(200, 200, 16,
false,
true);
1065 visualizer.disableMessages();
1068 auto sphere_center =
make_vec3(0, 0, 0);
1069 auto sphere_radius = 1.0f;
1075 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1079 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1083 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1086 SUBCASE(
"Navigation gizmo with printWindow") {
1087 Visualizer visualizer(200, 200, 16,
false,
true);
1088 visualizer.disableMessages();
1095 visualizer.showNavigationGizmo();
1096 std::string test_filename =
"test_nav_gizmo_screenshot.jpg";
1097 DOCTEST_CHECK_NOTHROW(visualizer.printWindow(test_filename.c_str()));
1100 DOCTEST_CHECK(std::filesystem::exists(test_filename));
1103 if (std::filesystem::exists(test_filename)) {
1104 std::filesystem::remove(test_filename);
1108 SUBCASE(
"Navigation gizmo state persists after printWindow") {
1109 Visualizer visualizer(200, 200, 16,
false,
true);
1110 visualizer.disableMessages();
1113 visualizer.showNavigationGizmo();
1116 std::string test_filename =
"test_nav_gizmo_persist.jpg";
1117 DOCTEST_CHECK_NOTHROW(visualizer.printWindow(test_filename.c_str()));
1120 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1123 if (std::filesystem::exists(test_filename)) {
1124 std::filesystem::remove(test_filename);
1129DOCTEST_TEST_CASE(
"GeometryHandler::getVertices coordinate system transformation") {
1134 Visualizer visualizer(200, 200, 16,
false,
true);
1136 SUBCASE(
"COORDINATES_WINDOW_NORMALIZED - getVertices should return original [0,1] coordinates") {
1142 DOCTEST_CHECK(rect_id != 0);
1145 auto vertices = visualizer.getGeometryVertices(rect_id);
1146 DOCTEST_CHECK(vertices.size() == 4);
1149 float half_width = size.
x * 0.5f;
1150 float half_height = size.
y * 0.5f;
1157 float tolerance = 1e-5f;
1158 DOCTEST_CHECK_MESSAGE(std::abs(vertices[0].x - expected_v0.
x) < tolerance,
"v0.x expected " << expected_v0.
x <<
" but got " << vertices[0].x);
1159 DOCTEST_CHECK_MESSAGE(std::abs(vertices[0].y - expected_v0.
y) < tolerance,
"v0.y expected " << expected_v0.
y <<
" but got " << vertices[0].y);
1160 DOCTEST_CHECK_MESSAGE(std::abs(vertices[1].x - expected_v1.
x) < tolerance,
"v1.x expected " << expected_v1.
x <<
" but got " << vertices[1].x);
1161 DOCTEST_CHECK_MESSAGE(std::abs(vertices[1].y - expected_v1.
y) < tolerance,
"v1.y expected " << expected_v1.
y <<
" but got " << vertices[1].y);
1164 for (
const auto &v: vertices) {
1165 bool x_in_range = (v.x >= 0.0f) && (v.x <= 1.0f);
1166 DOCTEST_CHECK_MESSAGE(x_in_range,
"Vertex x=" << v.x <<
" is outside [0,1] range - bug not fixed!");
1167 bool y_in_range = (v.y >= 0.0f) && (v.y <= 1.0f);
1168 DOCTEST_CHECK_MESSAGE(y_in_range,
"Vertex y=" << v.y <<
" is outside [0,1] range - bug not fixed!");
1172 SUBCASE(
"COORDINATES_CARTESIAN - getVertices should return original Cartesian coordinates") {
1178 DOCTEST_CHECK(rect_id != 0);
1181 auto vertices = visualizer.getGeometryVertices(rect_id);
1182 DOCTEST_CHECK(vertices.size() == 4);
1185 float half_width = size.
x * 0.5f;
1186 float half_height = size.
y * 0.5f;
1189 float tolerance = 1e-5f;
1190 DOCTEST_CHECK(std::abs(vertices[0].x - expected_v0.
x) < tolerance);
1191 DOCTEST_CHECK(std::abs(vertices[0].y - expected_v0.
y) < tolerance);
1192 DOCTEST_CHECK(std::abs(vertices[0].z - expected_v0.
z) < tolerance);
1196DOCTEST_TEST_CASE(
"GeometryHandler::setVertices coordinate system transformation") {
1199 Visualizer visualizer(200, 200, 16,
false,
true);
1201 SUBCASE(
"COORDINATES_WINDOW_NORMALIZED - setVertices should transform [0,1] to [-1,1]") {
1208 auto original_vertices = visualizer.getGeometryVertices(rect_id);
1211 std::vector<helios::vec3> new_vertices = original_vertices;
1212 for (
auto &v: new_vertices) {
1218 DOCTEST_CHECK_NOTHROW(visualizer.setGeometryVertices(rect_id, new_vertices));
1221 auto retrieved_vertices = visualizer.getGeometryVertices(rect_id);
1224 float tolerance = 1e-5f;
1225 DOCTEST_CHECK(std::abs(retrieved_vertices[0].x - new_vertices[0].x) < tolerance);
1226 DOCTEST_CHECK(std::abs(retrieved_vertices[0].y - new_vertices[0].y) < tolerance);
1229 SUBCASE(
"COORDINATES_CARTESIAN - setVertices should not transform") {
1236 auto original_vertices = visualizer.getGeometryVertices(rect_id);
1239 std::vector<helios::vec3> new_vertices = original_vertices;
1240 for (
auto &v: new_vertices) {
1246 DOCTEST_CHECK_NOTHROW(visualizer.setGeometryVertices(rect_id, new_vertices));
1249 auto retrieved_vertices = visualizer.getGeometryVertices(rect_id);
1252 float tolerance = 1e-5f;
1253 DOCTEST_CHECK(std::abs(retrieved_vertices[0].x - new_vertices[0].x) < tolerance);
1254 DOCTEST_CHECK(std::abs(retrieved_vertices[0].y - new_vertices[0].y) < tolerance);
1255 DOCTEST_CHECK(std::abs(retrieved_vertices[0].z - new_vertices[0].z) < tolerance);
1260 return helios::runDoctestWithValidation(argc, argv);