Files
Aethera/render_2D/tests/Plottable_Migration_Test.cpp
T
2026-08-21 22:12:47 +08:00

227 lines
11 KiB
C++

#include <render_2D/plottable/Plottables.hpp>
#include <render_2D/scene/Render_Scene_2D.hpp>
#include <gtest/gtest.h>
#include <array>
namespace {
using namespace aethera;
using namespace aethera::render_2d;
template <typename Object, typename... Args>
std::unique_ptr<Object> build_object(Args&&... args) {
typename Object::Builder builder(std::forward<Args>(args)...);
auto result = builder.build();
if (!result) std::terminate();
return std::move(result).value();
}
template <typename Scene, typename... Renderables>
std::unique_ptr<Scene> build_scene(Renderables*... renderables) {
typename Scene::Builder builder;
(builder.add_renderable(renderables), ...);
auto result = builder.build();
if (!result) std::terminate();
return std::move(result).value();
}
template <typename Axis>
void configure_axis(Axis* axis, Axis_Orientation orientation, Point_F position, Axis_Pixel_Length length, Size canvas) {
axis->template set<&Abs_Axis::Prop::orientation>(orientation);
axis->template set<&Abs_Axis::Prop::position>(position);
axis->template set<&Abs_Axis::Prop::pixel_length>(length);
}
}
TEST(plottable_migration, curve_plots_share_partitioned_rendering) {
using Scene = Impl<Render_Scene_2D>;
using Frequency = Impl<Frequency_Axis>;
using Numeric = Impl<Numeric_Axis>;
using Time = Impl<Time_Axis>;
using Trace = Impl<Frequency_Trace>;
using Sweep = Impl<Sweep_Spectrum>;
initialize_runtime(2);
const Size canvas{160, 120};
auto frequency = build_object<Frequency>();
auto power = build_object<Numeric>();
auto time = build_object<Time>();
auto value = build_object<Numeric>();
configure_axis(frequency.get(), Axis_Orientation::horizontal, {20.0, 100.0}, 120.0, canvas);
configure_axis(power.get(), Axis_Orientation::vertical, {20.0, 100.0}, -80.0, canvas);
configure_axis(time.get(), Axis_Orientation::horizontal, {20.0, 100.0}, 120.0, canvas);
configure_axis(value.get(), Axis_Orientation::vertical, {20.0, 100.0}, -80.0, canvas);
frequency->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{0.0, 100.0});
power->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{-100.0, 0.0});
value->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{0.0, 100.0});
auto trace = build_object<Trace>(time.get(), value.get());
auto sweep = build_object<Sweep>(frequency.get(), power.get());
trace->set<&Frequency_Trace::Prop::partition_mode>(Plot_Partition_Mode::fixed);
trace->set<&Frequency_Trace::Prop::partition_count>(2u);
trace->append_sample(time->append_time({1'000}), 10.0);
trace->append_sample(time->append_time({2'000}), 50.0);
trace->append_sample(time->append_time({3'000}), 90.0);
sweep->set<&Sweep_Spectrum::Prop::frequency_range>(Axis_Range{0.0, 100.0});
sweep->set<&Sweep_Spectrum::Prop::partition_mode>(Plot_Partition_Mode::fixed);
sweep->set<&Sweep_Spectrum::Prop::partition_count>(2u);
const std::array<Plot_Value, 4> block{-90.0, -60.0, -30.0, -10.0};
sweep->append_block(block);
auto scene = build_scene<Scene>(trace.get(), sweep.get());
scene->set<&Render_Scene_2D::Prop::viewport>(canvas);
scene->activate_view();
scene->render();
EXPECT_EQ(trace->sample_count(), 3u);
EXPECT_GT(trace->rendered_point_count(), 0u);
EXPECT_EQ(sweep->stored_block_count(), 1u);
EXPECT_GT(sweep->rendered_point_count(), 0u);
}
TEST(plottable_migration, raster_plots_share_partitioned_color_blocks) {
using Scene = Impl<Render_Scene_2D>;
using Frequency = Impl<Frequency_Axis>;
using Numeric = Impl<Numeric_Axis>;
using Time = Impl<Time_Axis>;
using Glow = Impl<Afterglow>;
using Fall = Impl<Waterfall>;
initialize_runtime(2);
const Size canvas{160, 120};
auto frequency = build_object<Frequency>();
auto power = build_object<Numeric>();
auto time = build_object<Time>();
configure_axis(frequency.get(), Axis_Orientation::horizontal, {20.0, 100.0}, 120.0, canvas);
configure_axis(power.get(), Axis_Orientation::vertical, {20.0, 100.0}, -80.0, canvas);
configure_axis(time.get(), Axis_Orientation::vertical, {20.0, 100.0}, -80.0, canvas);
frequency->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{0.0, 100.0});
power->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{-100.0, 0.0});
auto glow = build_object<Glow>(frequency.get(), power.get());
auto waterfall = build_object<Fall>(frequency.get(), time.get());
glow->set<&Afterglow::Prop::frequency_range>(Axis_Range{0.0, 100.0});
glow->set<&Afterglow::Prop::power_range>(Axis_Range{-100.0, 0.0});
glow->set<&Afterglow::Prop::power_point_size>(16u);
waterfall->set<&Waterfall::Prop::frequency_range>(Axis_Range{0.0, 100.0});
waterfall->set<&Waterfall::Prop::power_range>(Axis_Range{-100.0, 0.0});
const std::array<Plot_Value, 4> row{-90.0, -60.0, -30.0, -10.0};
glow->append_spectrum(row);
waterfall->append_row(time->append_time({1'000}), row);
waterfall->append_row(time->append_time({2'000}), row);
auto scene = build_scene<Scene>(glow.get(), waterfall.get());
scene->set<&Render_Scene_2D::Prop::viewport>(canvas);
scene->activate_view();
scene->render();
EXPECT_EQ(glow->history_count(), 1u);
EXPECT_GT(glow->rendered_cell_count(), 0u);
EXPECT_EQ(waterfall->row_count(), 2u);
EXPECT_GT(waterfall->rendered_cell_count(), 0u);
}
TEST(plottable_migration, direct_overlay_and_constellation_build_and_render) {
using Scene = Impl<Render_Scene_2D>;
using Numeric = Impl<Numeric_Axis>;
using Diagram = Impl<Constellation_Diagram>;
using Overlay = Impl<Selection_Rectangle_Overlay>;
initialize_runtime(2);
const Size canvas{160, 120};
auto horizontal = build_object<Numeric>();
auto vertical = build_object<Numeric>();
configure_axis(horizontal.get(), Axis_Orientation::horizontal, {20.0, 100.0}, 120.0, canvas);
configure_axis(vertical.get(), Axis_Orientation::vertical, {20.0, 100.0}, -80.0, canvas);
horizontal->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{-1.0, 1.0});
vertical->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{-1.0, 1.0});
auto diagram = build_object<Diagram>(horizontal.get(), vertical.get());
auto overlay = build_object<Overlay>(horizontal.get(), vertical.get());
diagram->set<&Constellation_Diagram::Prop::i_range>(Axis_Range{-1.0, 1.0});
diagram->set<&Constellation_Diagram::Prop::q_range>(Axis_Range{-1.0, 1.0});
diagram->append_point({0.25, -0.25});
auto scene = build_scene<Scene>(diagram.get(), overlay.get());
scene->set<&Render_Scene_2D::Prop::viewport>(canvas);
scene->activate_view();
scene->render();
EXPECT_EQ(diagram->point_count(), 1u);
EXPECT_EQ(overlay->read_state<Renderable::Base_Tag>().prepare_task_count, 0u);
EXPECT_EQ(overlay->read_state<Renderable::Base_Tag>().paint_task_count, 1u);
}
TEST(selection_overlay, control_extends_selection_and_plain_click_clears_it) {
using Scene = Impl<Render_Scene_2D>;
using Numeric = Impl<Numeric_Axis>;
using Overlay = Impl<Selection_Rectangle_Overlay>;
initialize_runtime(2);
const Size canvas{160, 120};
auto horizontal = build_object<Numeric>();
auto vertical = build_object<Numeric>();
configure_axis(horizontal.get(), Axis_Orientation::horizontal, {20.0, 100.0}, 120.0, canvas);
configure_axis(vertical.get(), Axis_Orientation::vertical, {20.0, 100.0}, -80.0, canvas);
horizontal->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{0.0, 100.0});
vertical->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{0.0, 100.0});
auto overlay = build_object<Overlay>(horizontal.get(), vertical.get());
auto scene = build_scene<Scene>(overlay.get());
scene->set<&Render_Scene_2D::Prop::viewport>(canvas);
scene->activate_view();
scene->render();
const auto drag = [&](Point_F first, Point_F second, Keyboard_Modifier modifiers) {
auto press = std::make_unique<Pointer_Event>(Event_Type::pointer_press);
press->position = first;
press->button = Mouse_Button::left;
press->modifiers = modifiers;
scene->dispatch_event(std::move(press));
auto move = std::make_unique<Pointer_Event>(Event_Type::pointer_move);
move->position = second;
move->modifiers = modifiers;
scene->dispatch_event(std::move(move));
auto release = std::make_unique<Pointer_Event>(Event_Type::pointer_release);
release->position = second;
release->button = Mouse_Button::left;
release->modifiers = modifiers;
scene->dispatch_event(std::move(release));
scene->render();
};
drag({30.0, 90.0}, {60.0, 60.0}, Keyboard_Modifier::none);
EXPECT_EQ(overlay->selected_regions().size(), 1u);
drag({70.0, 90.0}, {100.0, 60.0}, Keyboard_Modifier::control);
EXPECT_EQ(overlay->selected_regions().size(), 2u);
drag({120.0, 40.0}, {120.0, 40.0}, Keyboard_Modifier::none);
EXPECT_TRUE(overlay->selected_regions().empty());
}
TEST(selection_overlay, time_axis_selection_keeps_axis_coordinates_while_window_moves) {
using Scene = Impl<Render_Scene_2D>;
using Time = Impl<Time_Axis>;
using Numeric = Impl<Numeric_Axis>;
using Overlay = Impl<Selection_Rectangle_Overlay>;
initialize_runtime(2);
const Size canvas{160, 120};
auto time = build_object<Time>();
auto value = build_object<Numeric>();
configure_axis(time.get(), Axis_Orientation::horizontal, {20.0, 100.0}, 120.0, canvas);
configure_axis(value.get(), Axis_Orientation::vertical, {20.0, 100.0}, -80.0, canvas);
time->set<&Time_Axis::Prop::visible_count>(10);
value->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{0.0, 100.0});
time->append_time({1'000});
time->append_time({2'000});
time->append_time({3'000});
auto overlay = build_object<Overlay>(time.get(), value.get());
auto scene = build_scene<Scene>(overlay.get());
scene->set<&Render_Scene_2D::Prop::viewport>(canvas);
scene->activate_view();
scene->render();
auto press = std::make_unique<Pointer_Event>(Event_Type::pointer_press);
press->position = {92.0, 88.0};
press->button = Mouse_Button::left;
scene->dispatch_event(std::move(press));
auto move = std::make_unique<Pointer_Event>(Event_Type::pointer_move);
move->position = {116.0, 56.0};
scene->dispatch_event(std::move(move));
auto release = std::make_unique<Pointer_Event>(Event_Type::pointer_release);
release->position = {116.0, 56.0};
release->button = Mouse_Button::left;
scene->dispatch_event(std::move(release));
scene->render();
ASSERT_EQ(overlay->selected_regions().size(), 1u);
const Axis_Rectangle selected = overlay->selected_regions().front();
const auto pixel_before = time->coordinate_to_pixel(selected.horizontal.center());
time->append_time({4'000});
scene->render();
ASSERT_EQ(overlay->selected_regions().size(), 1u);
EXPECT_EQ(overlay->selected_regions().front(), selected);
EXPECT_NE(time->coordinate_to_pixel(selected.horizontal.center()), pixel_before);
}