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();
620 std::vector<uint> sphere_uuids =
context.addSphere(10,
make_vec3(0, 0, 0), 1.0f);
622 std::string test_material =
"test_visualizer_red_sphere";
623 if (!
context.doesMaterialExist(test_material)) {
624 context.addMaterial(test_material);
627 context.assignMaterialToPrimitive(sphere_uuids, test_material);
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();
699 std::vector<uint> sphere_uuids =
context.addSphere(10,
make_vec3(0, 0, 0), 1.0f);
701 std::string test_material =
"test_visualizer_red_sphere";
702 if (!
context.doesMaterialExist(test_material)) {
703 context.addMaterial(test_material);
706 context.assignMaterialToPrimitive(sphere_uuids, test_material);
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::headless render after windowed instance destroyed") {
818 Visualizer windowed(200, 200, 0,
true,
false);
819 windowed.disableMessages();
823 Visualizer headless(200, 200, 16,
true,
true);
824 headless.disableMessages();
826 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)};
831 DOCTEST_CHECK_NOTHROW(headless.plotUpdate(
true));
834TEST_CASE(
"Visualizer::PNG with transparent background (windowed mode)") {
841 std::string test_material =
"test_visualizer_red_patch";
842 if (!
context.doesMaterialExist(test_material)) {
843 context.addMaterial(test_material);
845 context.setMaterialTexture(test_material,
"plugins/visualizer/textures/AlmondLeaf.png");
846 context.setMaterialTextureColorOverride(test_material,
true);
848 context.assignMaterialToPrimitive(patch_UUID, test_material);
850 Visualizer visualizer(200, 200, 16,
false,
true);
851 visualizer.disableMessages();
854 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundTransparent());
860 DOCTEST_CHECK_NOTHROW(visualizer.buildContextGeometry(&
context));
863 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate());
866 std::string test_filename =
"test_transparent_bg_windowed.png";
867 DOCTEST_CHECK_NOTHROW(visualizer.printWindow(test_filename.c_str(),
"png"));
868 DOCTEST_CHECK(std::filesystem::exists(test_filename));
871 std::vector<helios::RGBAcolor> pixel_data;
873 DOCTEST_CHECK_NOTHROW(helios::readPNG(test_filename, width, height, pixel_data));
875 DOCTEST_CHECK(width > 0);
876 DOCTEST_CHECK(height > 0);
877 DOCTEST_CHECK(pixel_data.size() == width * height);
880 int transparent_pixels = 0;
881 int opaque_red_pixels = 0;
882 int checkerboard_pixels = 0;
884 for (
const auto &pixel: pixel_data) {
885 if (pixel.a < 0.1f) {
887 transparent_pixels++;
888 }
else if (pixel.a > 0.9f && pixel.r > 0.5f && pixel.g < 0.3f && pixel.b < 0.3f) {
891 }
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) {
893 checkerboard_pixels++;
898 DOCTEST_CHECK_MESSAGE(checkerboard_pixels == 0,
"Checkerboard texture should not appear in PNG output, got " << checkerboard_pixels <<
" checkerboard pixels");
901 uint total_pixels = width * height;
902 DOCTEST_CHECK_MESSAGE(transparent_pixels > total_pixels * 0.25,
"Expected significant transparent background area, got " << transparent_pixels <<
" transparent pixels out of " << total_pixels);
905 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);
908 if (std::filesystem::exists(test_filename)) {
909 std::filesystem::remove(test_filename);
913TEST_CASE(
"Visualizer::Transparent background with non-square window") {
922 std::string test_material =
"test_visualizer_red_patch_small";
923 if (!
context.doesMaterialExist(test_material)) {
924 context.addMaterial(test_material);
926 context.setMaterialTexture(test_material,
"plugins/visualizer/textures/AlmondLeaf.png");
927 context.setMaterialTextureColorOverride(test_material,
true);
929 context.assignMaterialToPrimitive(patch_UUID, test_material);
932 Visualizer visualizer(800, 600, 16,
false,
true);
933 visualizer.disableMessages();
936 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundTransparent());
939 DOCTEST_CHECK_NOTHROW(visualizer.buildContextGeometry(&
context));
942 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
944 std::string test_filename =
"test_transparent_bg_nonsquare.png";
945 DOCTEST_CHECK_NOTHROW(visualizer.printWindow(test_filename.c_str(),
"png"));
946 DOCTEST_CHECK(std::filesystem::exists(test_filename));
949 std::vector<helios::RGBAcolor> pixel_data;
951 DOCTEST_CHECK_NOTHROW(helios::readPNG(test_filename, width, height, pixel_data));
952 DOCTEST_CHECK(width > 0);
953 DOCTEST_CHECK(height > 0);
956 int transparent_pixels = 0;
957 int opaque_red_pixels = 0;
959 for (
const auto &pixel: pixel_data) {
960 if (pixel.a < 0.1f) {
961 transparent_pixels++;
962 }
else if (pixel.a > 0.9f && pixel.r > 0.5f && pixel.g < 0.3f && pixel.b < 0.3f) {
968 uint total_pixels = width * height;
969 DOCTEST_CHECK_MESSAGE(transparent_pixels > total_pixels * 0.5,
"Expected significant transparent background, got " << transparent_pixels <<
" transparent pixels out of " << total_pixels);
970 DOCTEST_CHECK_MESSAGE(opaque_red_pixels > 100,
"Expected visible red rectangle, got " << opaque_red_pixels <<
" red pixels");
973 if (std::filesystem::exists(test_filename)) {
974 std::filesystem::remove(test_filename);
978TEST_CASE(
"Visualizer::Background color/transparent switching") {
984 std::string test_material =
"test_visualizer_red_patch_small";
985 if (!
context.doesMaterialExist(test_material)) {
986 context.addMaterial(test_material);
988 context.setMaterialTexture(test_material,
"plugins/visualizer/textures/AlmondLeaf.png");
989 context.setMaterialTextureColorOverride(test_material,
true);
991 context.assignMaterialToPrimitive(patch_UUID, test_material);
993 SUBCASE(
"Watermark visible → transparent → solid color (should restore watermark)") {
994 Visualizer visualizer(200, 200, 16,
false,
true);
995 visualizer.disableMessages();
996 visualizer.buildContextGeometry(&
context);
1002 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundTransparent());
1005 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundColor(
make_RGBcolor(0.5f, 0.5f, 0.5f)));
1008 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1011 std::string test_filename =
"test_bg_switch_restore.png";
1012 DOCTEST_CHECK_NOTHROW(visualizer.printWindow(test_filename.c_str(),
"png"));
1013 DOCTEST_CHECK(std::filesystem::exists(test_filename));
1015 if (std::filesystem::exists(test_filename)) {
1016 std::filesystem::remove(test_filename);
1020 SUBCASE(
"Watermark hidden → transparent → solid color (should NOT restore watermark)") {
1021 Visualizer visualizer(200, 200, 16,
false,
true);
1022 visualizer.disableMessages();
1023 visualizer.buildContextGeometry(&
context);
1026 DOCTEST_CHECK_NOTHROW(visualizer.hideWatermark());
1029 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundTransparent());
1032 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundColor(
make_RGBcolor(0.5f, 0.5f, 0.5f)));
1035 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1037 std::string test_filename =
"test_bg_switch_no_restore.png";
1038 DOCTEST_CHECK_NOTHROW(visualizer.printWindow(test_filename.c_str(),
"png"));
1039 DOCTEST_CHECK(std::filesystem::exists(test_filename));
1041 if (std::filesystem::exists(test_filename)) {
1042 std::filesystem::remove(test_filename);
1046 SUBCASE(
"Multiple switches between transparent and solid") {
1047 Visualizer visualizer(200, 200, 16,
false,
true);
1048 visualizer.disableMessages();
1049 visualizer.buildContextGeometry(&
context);
1052 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundTransparent());
1053 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundColor(
make_RGBcolor(1.f, 0.f, 0.f)));
1054 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundTransparent());
1055 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundColor(
make_RGBcolor(0.f, 1.f, 0.f)));
1056 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundTransparent());
1057 DOCTEST_CHECK_NOTHROW(visualizer.setBackgroundColor(
make_RGBcolor(0.f, 0.f, 1.f)));
1059 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1063DOCTEST_TEST_CASE(
"Visualizer::Navigation Gizmo") {
1066 SUBCASE(
"Navigation gizmo is enabled by default") {
1067 Visualizer visualizer(200, 200, 16,
false,
true);
1068 visualizer.disableMessages();
1071 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1074 SUBCASE(
"Show and hide navigation gizmo") {
1075 Visualizer visualizer(200, 200, 16,
false,
true);
1076 visualizer.disableMessages();
1079 DOCTEST_CHECK_NOTHROW(visualizer.hideNavigationGizmo());
1080 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1083 DOCTEST_CHECK_NOTHROW(visualizer.showNavigationGizmo());
1084 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1087 DOCTEST_CHECK_NOTHROW(visualizer.hideNavigationGizmo());
1088 DOCTEST_CHECK_NOTHROW(visualizer.showNavigationGizmo());
1089 DOCTEST_CHECK_NOTHROW(visualizer.hideNavigationGizmo());
1090 DOCTEST_CHECK_NOTHROW(visualizer.showNavigationGizmo());
1091 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1094 SUBCASE(
"Navigation gizmo with camera movement") {
1095 Visualizer visualizer(200, 200, 16,
false,
true);
1096 visualizer.disableMessages();
1099 auto sphere_center =
make_vec3(0, 0, 0);
1100 auto sphere_radius = 1.0f;
1106 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1110 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1114 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1117 SUBCASE(
"Navigation gizmo with printWindow") {
1118 Visualizer visualizer(200, 200, 16,
false,
true);
1119 visualizer.disableMessages();
1126 visualizer.showNavigationGizmo();
1127 std::string test_filename =
"test_nav_gizmo_screenshot.jpg";
1128 DOCTEST_CHECK_NOTHROW(visualizer.printWindow(test_filename.c_str()));
1131 DOCTEST_CHECK(std::filesystem::exists(test_filename));
1134 if (std::filesystem::exists(test_filename)) {
1135 std::filesystem::remove(test_filename);
1139 SUBCASE(
"Navigation gizmo state persists after printWindow") {
1140 Visualizer visualizer(200, 200, 16,
false,
true);
1141 visualizer.disableMessages();
1144 visualizer.showNavigationGizmo();
1147 std::string test_filename =
"test_nav_gizmo_persist.jpg";
1148 DOCTEST_CHECK_NOTHROW(visualizer.printWindow(test_filename.c_str()));
1151 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1154 if (std::filesystem::exists(test_filename)) {
1155 std::filesystem::remove(test_filename);
1160DOCTEST_TEST_CASE(
"GeometryHandler::getVertices coordinate system transformation") {
1165 Visualizer visualizer(200, 200, 16,
false,
true);
1167 SUBCASE(
"COORDINATES_WINDOW_NORMALIZED - getVertices should return original [0,1] coordinates") {
1173 DOCTEST_CHECK(rect_id != 0);
1176 auto vertices = visualizer.getGeometryVertices(rect_id);
1177 DOCTEST_CHECK(vertices.size() == 4);
1180 float half_width = size.
x * 0.5f;
1181 float half_height = size.
y * 0.5f;
1188 float tolerance = 1e-5f;
1189 DOCTEST_CHECK_MESSAGE(std::abs(vertices[0].x - expected_v0.
x) < tolerance,
"v0.x expected " << expected_v0.
x <<
" but got " << vertices[0].x);
1190 DOCTEST_CHECK_MESSAGE(std::abs(vertices[0].y - expected_v0.
y) < tolerance,
"v0.y expected " << expected_v0.
y <<
" but got " << vertices[0].y);
1191 DOCTEST_CHECK_MESSAGE(std::abs(vertices[1].x - expected_v1.
x) < tolerance,
"v1.x expected " << expected_v1.
x <<
" but got " << vertices[1].x);
1192 DOCTEST_CHECK_MESSAGE(std::abs(vertices[1].y - expected_v1.
y) < tolerance,
"v1.y expected " << expected_v1.
y <<
" but got " << vertices[1].y);
1195 for (
const auto &v: vertices) {
1196 bool x_in_range = (v.x >= 0.0f) && (v.x <= 1.0f);
1197 DOCTEST_CHECK_MESSAGE(x_in_range,
"Vertex x=" << v.x <<
" is outside [0,1] range - bug not fixed!");
1198 bool y_in_range = (v.y >= 0.0f) && (v.y <= 1.0f);
1199 DOCTEST_CHECK_MESSAGE(y_in_range,
"Vertex y=" << v.y <<
" is outside [0,1] range - bug not fixed!");
1203 SUBCASE(
"COORDINATES_CARTESIAN - getVertices should return original Cartesian coordinates") {
1209 DOCTEST_CHECK(rect_id != 0);
1212 auto vertices = visualizer.getGeometryVertices(rect_id);
1213 DOCTEST_CHECK(vertices.size() == 4);
1216 float half_width = size.
x * 0.5f;
1217 float half_height = size.
y * 0.5f;
1220 float tolerance = 1e-5f;
1221 DOCTEST_CHECK(std::abs(vertices[0].x - expected_v0.
x) < tolerance);
1222 DOCTEST_CHECK(std::abs(vertices[0].y - expected_v0.
y) < tolerance);
1223 DOCTEST_CHECK(std::abs(vertices[0].z - expected_v0.
z) < tolerance);
1227DOCTEST_TEST_CASE(
"GeometryHandler::setVertices coordinate system transformation") {
1230 Visualizer visualizer(200, 200, 16,
false,
true);
1232 SUBCASE(
"COORDINATES_WINDOW_NORMALIZED - setVertices should transform [0,1] to [-1,1]") {
1239 auto original_vertices = visualizer.getGeometryVertices(rect_id);
1242 std::vector<helios::vec3> new_vertices = original_vertices;
1243 for (
auto &v: new_vertices) {
1249 DOCTEST_CHECK_NOTHROW(visualizer.setGeometryVertices(rect_id, new_vertices));
1252 auto retrieved_vertices = visualizer.getGeometryVertices(rect_id);
1255 float tolerance = 1e-5f;
1256 DOCTEST_CHECK(std::abs(retrieved_vertices[0].x - new_vertices[0].x) < tolerance);
1257 DOCTEST_CHECK(std::abs(retrieved_vertices[0].y - new_vertices[0].y) < tolerance);
1260 SUBCASE(
"COORDINATES_CARTESIAN - setVertices should not transform") {
1267 auto original_vertices = visualizer.getGeometryVertices(rect_id);
1270 std::vector<helios::vec3> new_vertices = original_vertices;
1271 for (
auto &v: new_vertices) {
1277 DOCTEST_CHECK_NOTHROW(visualizer.setGeometryVertices(rect_id, new_vertices));
1280 auto retrieved_vertices = visualizer.getGeometryVertices(rect_id);
1283 float tolerance = 1e-5f;
1284 DOCTEST_CHECK(std::abs(retrieved_vertices[0].x - new_vertices[0].x) < tolerance);
1285 DOCTEST_CHECK(std::abs(retrieved_vertices[0].y - new_vertices[0].y) < tolerance);
1286 DOCTEST_CHECK(std::abs(retrieved_vertices[0].z - new_vertices[0].z) < tolerance);
1290DOCTEST_TEST_CASE(
"Visualizer::colorContextPrimitivesByObjectData orphan primitives use colormap-at-0") {
1300 DOCTEST_CHECK(
context.getPrimitiveParentObjectID(patch_UUID) == 0);
1302 Visualizer visualizer(200, 200, 16,
false,
true);
1303 visualizer.disableMessages();
1308 visualizer.setColorbarRange(1.f, 2.f);
1310 DOCTEST_CHECK_NOTHROW(visualizer.buildContextGeometry(&
context));
1311 DOCTEST_CHECK_NOTHROW(visualizer.colorContextPrimitivesByObjectData(
"nonexistent_object_data"));
1314 DOCTEST_CHECK_NOTHROW(visualizer.disableColorbar());
1315 DOCTEST_CHECK_NOTHROW(visualizer.plotUpdate(
true));
1317 std::string test_filename =
"test_orphan_color_by_object_data.png";
1318 DOCTEST_CHECK_NOTHROW(visualizer.printWindow(test_filename.c_str(),
"png"));
1319 DOCTEST_CHECK(std::filesystem::exists(test_filename));
1321 std::vector<RGBAcolor> pixel_data;
1323 DOCTEST_CHECK_NOTHROW(helios::readPNG(test_filename, width, height, pixel_data));
1328 int black_patch_pixels = 0;
1329 for (
const auto &pixel: pixel_data) {
1330 if (pixel.r > 0.5f && pixel.g < 0.2f && pixel.b < 0.2f) {
1332 }
else if (pixel.r < 0.1f && pixel.g < 0.1f && pixel.b < 0.1f) {
1333 black_patch_pixels++;
1337 DOCTEST_CHECK_MESSAGE(red_pixels == 0,
"Orphan primitive rendered with its base RGBA (red) instead of colormap-at-0; got " << red_pixels <<
" red pixels");
1338 DOCTEST_CHECK_MESSAGE(black_patch_pixels > 100,
"Expected the orphan patch to render as near-black (colormap_hot at 0); only got " << black_patch_pixels <<
" black pixels");
1340 if (std::filesystem::exists(test_filename)) {
1341 std::filesystem::remove(test_filename);
1346 return helios::runDoctestWithValidation(argc, argv);