// Tests para la logica matematica de pie_chart (slice hit-testing). // // La funcion `slice_at` real vive en namespace anonimo en pie_chart.cpp y // esta tightly coupled con ImPlot. Aqui reproducimos el algoritmo y lo // testeamos para garantizar que la matematica es correcta. Si la implementacion // real se refactoriza para exponer la logica pura, este test se actualiza // para usar el header publico (ver issue 0048 / refactor pendiente). #define CATCH_CONFIG_MAIN #include "catch_amalgamated.hpp" #include namespace { // Mismo algoritmo que pie_chart.cpp::slice_at (verbatim). template int slice_at(const T* values, int count, double total, double mouse_x, double mouse_y, double cx, double cy, double radius) { double dx = mouse_x - cx; double dy = mouse_y - cy; double r = std::sqrt(dx * dx + dy * dy); if (r > radius) return -1; constexpr double kPI = 3.14159265358979323846; double angle_deg = std::atan2(dy, dx) * 180.0 / kPI; double offset = angle_deg - 90.0; while (offset < 0.0) offset += 360.0; while (offset >= 360.0) offset -= 360.0; double acc = 0.0; for (int i = 0; i < count; i++) { double sweep = (static_cast(values[i]) / total) * 360.0; if (offset >= acc && offset < acc + sweep) return i; acc += sweep; } return count - 1; } } // namespace TEST_CASE("pie_chart::slice_at returns -1 outside radius", "[pie_chart]") { double values[] = {1.0, 1.0, 1.0, 1.0}; double total = 4.0; // Mouse muy lejos del centro. REQUIRE(slice_at(values, 4, total, 5.0, 5.0, 0.5, 0.5, 0.4) == -1); } TEST_CASE("pie_chart::slice_at on cursor at center returns first slice", "[pie_chart]") { double values[] = {1.0, 1.0, 1.0, 1.0}; double total = 4.0; // En el centro, dx=dy=0 -> atan2(0,0)=0 -> offset=-90 -> 270. // Con 4 slices iguales (cada una 90 grados), 270 cae en la 4a slice (idx 3). int idx = slice_at(values, 4, total, 0.5, 0.5, 0.5, 0.5, 0.4); REQUIRE(idx >= 0); REQUIRE(idx < 4); } TEST_CASE("pie_chart::slice_at directly above center returns first slice", "[pie_chart]") { // 4 slices iguales. offset=0 (arriba) cae en idx 0. double values[] = {1.0, 1.0, 1.0, 1.0}; int idx = slice_at(values, 4, 4.0, 0.5, 0.7, 0.5, 0.5, 0.4); REQUIRE(idx == 0); } TEST_CASE("pie_chart::slice_at right of center hits second slice (CCW)", "[pie_chart]") { // ImPlot dibuja CCW desde 90 grados (arriba). Yendo CCW: arriba -> izq -> abajo -> der. // Mouse a la derecha: offset = atan2(0, +x)=0 deg -> -90 -> 270. // 4 slices: [0,90)=0, [90,180)=1, [180,270)=2, [270,360)=3 -> idx 3. double values[] = {1.0, 1.0, 1.0, 1.0}; int idx = slice_at(values, 4, 4.0, 0.7, 0.5, 0.5, 0.5, 0.4); REQUIRE(idx == 3); } TEST_CASE("pie_chart::slice_at single slice always returns 0", "[pie_chart]") { double values[] = {1.0}; REQUIRE(slice_at(values, 1, 1.0, 0.5, 0.7, 0.5, 0.5, 0.4) == 0); REQUIRE(slice_at(values, 1, 1.0, 0.7, 0.5, 0.5, 0.5, 0.4) == 0); REQUIRE(slice_at(values, 1, 1.0, 0.5, 0.3, 0.5, 0.5, 0.4) == 0); } TEST_CASE("pie_chart::slice_at right at radius edge", "[pie_chart]") { double values[] = {1.0, 1.0}; // Justo en el borde del radio: r == radius -> r > radius es false, hits. int idx = slice_at(values, 2, 2.0, 0.5 + 0.4, 0.5, 0.5, 0.5, 0.4); REQUIRE(idx >= 0); // Justo fuera del borde. REQUIRE(slice_at(values, 2, 2.0, 0.5 + 0.41, 0.5, 0.5, 0.5, 0.4) == -1); } TEST_CASE("pie_chart::slice_at unequal slices distributes proportionally", "[pie_chart]") { // Una slice grande (75%) y otra pequena (25%). double values[] = {3.0, 1.0}; double total = 4.0; // Slice 0: offset [0, 270). Slice 1: offset [270, 360). // Mouse arriba (offset=0) -> idx 0. REQUIRE(slice_at(values, 2, total, 0.5, 0.7, 0.5, 0.5, 0.4) == 0); }