#include #include #include namespace { using namespace aethera; using namespace aethera::render_2d; template std::unique_ptr make_axis(Axis_Orientation orientation, Axis_Range range) { typename Axis::Builder builder; builder .set(&Abs_Axis::Prop::position, Point_F{10.0, 90.0}) .set(&Abs_Axis::Prop::pixel_length, orientation == Axis_Orientation::horizontal ? 100.0 : -80.0) .set(&Abs_Axis::Prop::orientation, orientation); if constexpr (std::derived_from) builder.template set(&Numeric_Axis::Prop::coordinate_range, range); auto axis = builder.build(); aethera::detail::model_private(*axis).advance(); return axis; } TEST(Plottable_Migration, Frequency_Trace_And_Sweep_Use_Import_Lists) { auto time = Time_Axis::Builder{}.build(); auto value = make_axis(Axis_Orientation::vertical, {0.0, 100.0}); time->set(&Abs_Axis::Prop::position, Point_F{10.0, 90.0}); time->set(&Abs_Axis::Prop::pixel_length, 100.0); time->append_time({1'000}); time->append_time({2'000}); aethera::detail::model_private(*time).advance(); Frequency_Trace::Builder trace_builder(*time, *value); auto trace = trace_builder.build(); trace->set([](Frequency_Trace::Samples& samples) { samples.push_back({0, 10.0}); samples.push_back({1, 80.0}); }); aethera::detail::model_private(*trace).advance(); Frequency_Trace::State trace_state; trace->get([&](const Frequency_Trace::State& state) { trace_state = state; }); EXPECT_EQ(trace_state.sample_count, 2U); auto frequency = make_axis(Axis_Orientation::horizontal, {0.0, 100.0}); auto power = make_axis(Axis_Orientation::vertical, {-100.0, 0.0}); Sweep_Spectrum::Builder sweep_builder(*frequency, *power); sweep_builder .set(&Sweep_Spectrum::Prop::frequency_range, Axis_Range{0.0, 100.0}) .set(&Sweep_Spectrum::Prop::block_count, 2U); auto sweep = sweep_builder.build(); sweep->set([](Sweep_Spectrum::Blocks& blocks) { blocks.push_back(std::make_shared(Sweep_Spectrum_Block{0, {-90.0, -40.0}})); blocks.push_back(std::make_shared(Sweep_Spectrum_Block{1, {-30.0, -10.0}})); }); aethera::detail::model_private(*sweep).advance(); Sweep_Spectrum::State sweep_state; sweep->get([&](const Sweep_Spectrum::State& state) { sweep_state = state; }); EXPECT_EQ(sweep_state.stored_block_count, 2U); } TEST(Plottable_Migration, Raster_Plots_Keep_One_Private_History) { auto frequency = make_axis(Axis_Orientation::horizontal, {0.0, 100.0}); auto power = make_axis(Axis_Orientation::vertical, {-100.0, 0.0}); Afterglow::Builder glow_builder(*frequency, *power); glow_builder .set(&Afterglow::Prop::frequency_range, Axis_Range{0.0, 100.0}) .set(&Afterglow::Prop::power_range, Axis_Range{-100.0, 0.0}) .set(&Afterglow::Prop::power_point_size, 16U) .set(&Afterglow::Prop::partition_grid, Plot_Partition_Grid{2, 2}); auto glow = glow_builder.build(); glow->set([](Afterglow::Spectra& spectra) { spectra.push_back(std::make_shared>(std::vector{-90.0, -60.0, -30.0, -10.0})); }); aethera::detail::model_private(*glow).advance(); Afterglow::State glow_state; glow->get([&](const Afterglow::State& state) { glow_state = state; }); EXPECT_EQ(glow_state.spectrum_count, 1U); auto time = Time_Axis::Builder{}.build(); time->set(&Abs_Axis::Prop::position, Point_F{10.0, 90.0}); time->set(&Abs_Axis::Prop::pixel_length, -80.0); time->set(&Abs_Axis::Prop::orientation, Axis_Orientation::vertical); const auto first_tick = time->append_time({1'000}); const auto second_tick = time->append_time({2'000}); aethera::detail::model_private(*time).advance(); Waterfall::Builder waterfall_builder(*frequency, *time); waterfall_builder .set(&Waterfall::Prop::frequency_range, Axis_Range{0.0, 100.0}) .set(&Waterfall::Prop::power_range, Axis_Range{-100.0, 0.0}) .set(&Waterfall::Prop::partition_grid, Plot_Partition_Grid{2, 2}); auto waterfall = waterfall_builder.build(); waterfall->set([first_tick, second_tick](Waterfall::Rows& rows) { rows.push_back(std::make_shared(Waterfall_Row{first_tick, {-90.0, -60.0, -30.0, -10.0}})); rows.push_back(std::make_shared(Waterfall_Row{second_tick, {-80.0, -50.0, -20.0, -5.0}})); }); aethera::detail::model_private(*waterfall).advance(); Waterfall::State waterfall_state; waterfall->get([&](const Waterfall::State& state) { waterfall_state = state; }); EXPECT_EQ(waterfall_state.row_count, 2U); } TEST(Plottable_Migration, Constellation_And_Overlay_Use_Virtual_Private_Runtime) { auto horizontal = make_axis(Axis_Orientation::horizontal, {-1.0, 1.0}); auto vertical = make_axis(Axis_Orientation::vertical, {-1.0, 1.0}); const auto now = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); Constellation_Diagram::Builder diagram_builder(*horizontal, *vertical); diagram_builder .set(&Constellation_Diagram::Prop::i_range, Axis_Range{-1.0, 1.0}) .set(&Constellation_Diagram::Prop::q_range, Axis_Range{-1.0, 1.0}); auto diagram = diagram_builder.build(); diagram->set([now](Constellation_Diagram::Points& points) { points.push_back({{0.25, -0.25}, now}); }); aethera::detail::model_private(*diagram).advance(); Constellation_Diagram::State diagram_state; diagram->get([&](const Constellation_Diagram::State& state) { diagram_state = state; }); EXPECT_EQ(diagram_state.point_count, 1U); Selection_Rectangle_Overlay::Builder overlay_builder(*horizontal, *vertical); auto overlay = overlay_builder.build(); auto& overlay_private = aethera::detail::model_private(*overlay); Pointer_Event press{Event_Type::pointer_press}; press.position = {20.0, 80.0}; press.button = Mouse_Button::left; overlay_private.dispatch_event(press); Pointer_Event move{Event_Type::pointer_move}; move.position = {60.0, 40.0}; overlay_private.dispatch_event(move); Pointer_Event release{Event_Type::pointer_release}; release.position = move.position; release.button = Mouse_Button::left; overlay_private.dispatch_event(release); overlay_private.advance(); std::size_t selected{}; overlay->get([&](const Selection_Rectangle_Overlay::Prop& properties) { selected = properties.selected_regions.size(); }); EXPECT_EQ(selected, 1U); } } // namespace