#include "Core2/export.h" #include "Core2/plottable/Curve_Sampling.h" #include "Core2/render/Blend2D_Cache.h" #include #include #include #include #include namespace renderive { namespace { TEST(Renderive_Core2, RootIdentityAndRefreshDiagnosticsComeFromKernelState) { Plot_Core plot; plot.init(); const auto root = plot.root_renderable(); ASSERT_TRUE(root); root->set_object_name("renamed-root"); EXPECT_EQ(plot.root_renderable(), root); plot.set_viewport_size({32, 24}); plot.activate_view(); plot.set_max_render_fps(144.0); ASSERT_TRUE(plot.render_frame()); const auto diagnostics = plot.diagnostics(); EXPECT_DOUBLE_EQ(diagnostics.refresh.frequency_hz, 144.0); EXPECT_EQ(diagnostics.refresh.frame_count, 1u); } TEST(Renderive_Core2, EveryCurveInterpolationModeHasDistinctSamplingSemantics) { const std::array edge_values{0.0, 10.0}; const Range domain{0.0, 1.0}; const auto nearest = detail::curve_sampling::interpolate( edge_values, domain, Line_Interpolation_Mode::Nearest_Sample); const auto linear = detail::curve_sampling::interpolate( edge_values, domain, Line_Interpolation_Mode::Linear_Value); const auto power = detail::curve_sampling::interpolate( edge_values, domain, Line_Interpolation_Mode::Linear_Power_Domain); const auto step_left = detail::curve_sampling::interpolate( edge_values, domain, Line_Interpolation_Mode::Step_Left); const auto step_right = detail::curve_sampling::interpolate( edge_values, domain, Line_Interpolation_Mode::Step_Right); EXPECT_EQ(nearest.size(), 4u); EXPECT_EQ(linear.size(), 2u); ASSERT_EQ(power.size(), 5u); EXPECT_NEAR(power[2].value, 7.4036269, 1e-6); ASSERT_EQ(step_left.size(), 3u); ASSERT_EQ(step_right.size(), 3u); EXPECT_DOUBLE_EQ(step_left[1].coordinate, 1.0); EXPECT_DOUBLE_EQ(step_left[1].value, 0.0); EXPECT_DOUBLE_EQ(step_right[1].coordinate, 0.0); EXPECT_DOUBLE_EQ(step_right[1].value, 10.0); const std::array curved_values{0.0, 10.0, 0.0, 10.0}; const auto cubic = detail::curve_sampling::interpolate( curved_values, {0.0, 3.0}, Line_Interpolation_Mode::Cubic_Value); ASSERT_EQ(cubic.size(), 13u); EXPECT_NEAR(cubic[2].value, 5.625, 1e-9); const std::array visible_values{}; const Axis_Transform visible_x{{4.0, 6.0}, 0.0, 100.0}; const Axis_Transform visible_y{{0.0, 1.0}, 0.0, 100.0}; const auto clipped = detail::curve_points( visible_values, {0.0, 10.0}, visible_x, visible_y, true, Line_Interpolation_Mode::Linear_Value); EXPECT_LT(clipped.size(), visible_values.size()); ASSERT_GE(clipped.size(), 2u); EXPECT_LE(clipped.front().x, 0.0); EXPECT_GE(clipped.back().x, 100.0); } TEST(Renderive_Core2, BicubicHeatmapUsesRealCubicResampling) { const std::array source{ pack_rgba(255, 0, 0), pack_rgba(0, 0, 0), pack_rgba(255, 255, 255), pack_rgba(0, 0, 255), pack_rgba(0, 255, 0), pack_rgba(255, 255, 255), pack_rgba(0, 0, 0), pack_rgba(255, 0, 0), pack_rgba(0, 0, 255), pack_rgba(0, 0, 0), pack_rgba(255, 255, 255), pack_rgba(0, 255, 0), pack_rgba(255, 255, 255), pack_rgba(255, 0, 0), pack_rgba(0, 255, 0), pack_rgba(0, 0, 0) }; const auto render = [&source](Image_Interpolation_Mode interpolation) { detail::Blend2D_Color_Cache cache; { detail::Painter painter(cache, {11, 11}); painter.heatmap({0.0, 0.0, 11.0, 11.0}, 4, 4, source, interpolation); } const Image_View view = cache.view(); std::vector pixels; pixels.reserve(static_cast(view.width) * view.height); for (int y = 0; y < view.height; ++y) { const auto* row = reinterpret_cast( view.data + static_cast(y) * view.stride); pixels.insert(pixels.end(), row, row + view.width); } return pixels; }; const auto bilinear = render(Image_Interpolation_Mode::Bilinear); const auto bicubic = render(Image_Interpolation_Mode::Bicubic); EXPECT_EQ(bilinear.size(), bicubic.size()); EXPECT_NE(bilinear, bicubic); } TEST(Renderive_Core2, KernelSceneRendersBusinessObjectsIntoBlend2DFrame) { Plot_Core plot; plot.init(); plot.set_viewport_size({320, 180}); plot.activate_view(); const auto root = plot.root_renderable(); ASSERT_TRUE(root); auto frequency_axis = Frequency_Axis::Builder(root, Orientation::Horizontal) .set_x(32) .set_y(150) .set_pixel_length(270) .set_coord_range({88.0, 108.0}) .build(); frequency_axis->set_locale({','}); frequency_axis->set_label_rotation_degrees(15); EXPECT_EQ(frequency_axis->tick_label(88.5), "88,5 Hz"); frequency_axis->set_label_precision(4); EXPECT_EQ(frequency_axis->tick_label(1'234'567.0), "1,2346 MHz"); auto power_axis = Axis::Builder(root, Orientation::Vertical) .set_x(32) .set_y(8) .set_pixel_length(142) .set_coord_range({-120.0, 0.0}) .build(); auto spectrum = Spectrum::Builder(root, frequency_axis, power_axis) .set_frequency_range({88.0, 108.0}) .set_frequency_point_size(64) .set_max_hold_visible(true) .build(); std::vector samples(64); for (std::size_t index = 0; index < samples.size(); ++index) samples[index] = -100.0 + static_cast(index % 24) * 3.0; spectrum->update_samples(samples); ASSERT_TRUE(plot.render_frame()); bool saw_frame = false; bool saw_drawn_pixel = false; plot.with_frame([&](Image_View view) { saw_frame = !view.empty() && view.width == 320 && view.height == 180; if (!saw_frame) return; for (int y = 0; y < view.height && !saw_drawn_pixel; ++y) { const auto* row = view.data + static_cast(y) * view.stride; for (int x = 0; x < view.width * static_cast(sizeof(Pixel)); ++x) { if (row[x] != std::byte{}) { saw_drawn_pixel = true; break; } } } }); EXPECT_TRUE(saw_frame); EXPECT_TRUE(saw_drawn_pixel); EXPECT_EQ(plot.diagnostics().refresh.frame_count, 1u); EXPECT_FALSE(plot.render_frame()); spectrum->update_samples(samples); EXPECT_TRUE(plot.render_frame()); frequency_axis->set_use_wheel(true); const Range before_zoom = frequency_axis->coord_range(); Wheel_Event wheel; wheel.position = {160.0, 150.0}; wheel.angle_delta_y = 120.0; plot.dispatch_event(wheel); EXPECT_TRUE(wheel.is_accepted()); EXPECT_LT(frequency_axis->coord_range().size(), before_zoom.size()); EXPECT_TRUE(plot.render_frame()); plot.remove_renderable(spectrum); EXPECT_TRUE(plot.render_frame(true)); EXPECT_FALSE(plot.render_frame()); } TEST(Renderive_Core2, TimeAxisUsesOneFontStateAndFormatsConfiguredLabels) { Plot_Core plot; plot.init(); const auto root = plot.root_renderable(); auto axis = Time_Axis::Builder(root, Orientation::Horizontal) .set_time_format("hh-mm-ss.zzz") .set_font(Font{18.0, 700, true}) .set_tick_label_spacing_px(17) .build(); ASSERT_TRUE(axis); const int tick = axis->append_time(Time_Of_Day{3'723'045}); EXPECT_EQ(axis->tick_label(tick), "01-02-03.045"); EXPECT_EQ(axis->font(), (Font{18.0, 700, true})); EXPECT_EQ(axis->unit_text_font(), axis->font()); EXPECT_EQ(axis->tick_label_spacing_px(), 17); } TEST(Renderive_Core2, PerformanceOverlayConsumesKernelFrameDiagnostics) { Plot_Core plot; plot.init(); plot.set_viewport_size({160, 90}); plot.activate_view(); plot.set_max_render_fps(120.0); Performance_Overlay_Options options; options.log.enabled = false; const auto overlay = attach_performance_overlay(plot, options); ASSERT_TRUE(overlay); set_performance_plot_name(plot, "integration"); set_performance_overlay_enabled(plot, true); ASSERT_TRUE(plot.render_frame(true)); const auto snapshot = overlay->display_snapshot(); ASSERT_TRUE(snapshot); EXPECT_EQ(snapshot->plot_name, "integration"); EXPECT_EQ(snapshot->viewport_size, (Size{160, 90})); EXPECT_FALSE(snapshot->lines.empty()); EXPECT_EQ(snapshot->frame_count, 1u); EXPECT_DOUBLE_EQ(plot.max_render_fps(), 120.0); } TEST(Renderive_Core2, AllFrameControlModesUseKernelStrategiesAndExposeObservers) { Plot_Core manual(Frame_Control_Mode::Manual); manual.init(); manual.set_viewport_size({64, 40}); EXPECT_EQ(manual.frame_control_mode(), Frame_Control_Mode::Manual); ASSERT_TRUE(manual.prepare_frame()); auto manual_observer = manual.frame_observer_snapshot(); EXPECT_EQ(manual_observer.last_event, "prepared"); EXPECT_EQ(manual_observer.pending_frame_count, 1u); ASSERT_TRUE(manual.refresh_manual_frame()); ASSERT_TRUE(manual.render_prepared_frame()); manual_observer = manual.frame_observer_snapshot(); EXPECT_EQ(manual_observer.last_event, "rendered"); EXPECT_EQ(manual_observer.consumed_frame_count, 1u); Plot_Core low_latency(Frame_Control_Mode::Low_Latency); low_latency.init(); low_latency.set_viewport_size({64, 40}); low_latency.activate_view(); ASSERT_TRUE(low_latency.render_frame(true)); const auto low_observer = low_latency.frame_observer_snapshot(); EXPECT_EQ(low_latency.frame_control_mode(), Frame_Control_Mode::Low_Latency); EXPECT_EQ(low_observer.last_event, "lifecycle_completed"); EXPECT_EQ(low_observer.consumed_frame_count, 1u); Plot_Core playback(Frame_Control_Mode::Playback); playback.init(); playback.set_viewport_size({64, 40}); ASSERT_TRUE(playback.prepare_frame()); ASSERT_TRUE(playback.prepare_frame()); ASSERT_TRUE(playback.prepare_frame()); auto playback_observer = playback.frame_observer_snapshot(); EXPECT_EQ(playback.frame_control_mode(), Frame_Control_Mode::Playback); EXPECT_EQ(playback_observer.last_event, "enqueued"); EXPECT_EQ(playback_observer.pending_frame_count, 3u); ASSERT_TRUE(playback.render_prepared_frame()); playback_observer = playback.frame_observer_snapshot(); EXPECT_EQ(playback_observer.last_event, "rendered"); EXPECT_EQ(playback_observer.pending_frame_count, 2u); EXPECT_EQ(playback_observer.consumed_frame_count, 1u); } TEST(Renderive_Core2, RetainedAxisAndTimeAxisApisRoundTripWithoutWebAdapters) { Plot_Core plot; plot.init(); const auto root = plot.root_renderable(); ASSERT_TRUE(root); const auto axis = Axis::Builder(root, Orientation::Vertical) .set_x(31) .set_y(17) .set_pixel_length(240) .set_tick_length(13) .set_sub_tick_length(7) .set_color({10, 20, 30, 255}) .set_unit_text("dB") .set_unit_text_font({15.0, 650, true}) .set_unit_text_pen({Color{40, 50, 60, 255}, 2.0}) .set_unit_text_background_brush( {Color{3, 4, 5, 255}, Brush_Style::Solid}) .set_label_rotation_degrees(27) .set_coord_range({-120.0, -20.0}) .set_label_precision(4) .set_use_wheel(true) .set_use_drag(true) .build(); ASSERT_TRUE(axis); EXPECT_EQ(axis->x(), 31); EXPECT_EQ(axis->y(), 17); EXPECT_EQ(axis->orientation(), Orientation::Vertical); EXPECT_EQ(axis->pixel_length(), 240U); EXPECT_EQ(axis->tick_length(), 13); EXPECT_EQ(axis->sub_tick_length(), 7); EXPECT_EQ(axis->color(), (Color{10, 20, 30, 255})); EXPECT_EQ(axis->unit_text(), "dB"); EXPECT_EQ(axis->unit_text_font(), (Font{15.0, 650, true})); EXPECT_EQ(axis->unit_text_pen(), (Pen{Color{40, 50, 60, 255}, 2.0})); EXPECT_EQ(axis->unit_text_background_brush(), (Brush{Color{3, 4, 5, 255}, Brush_Style::Solid})); EXPECT_EQ(axis->label_rotation_degrees(), 27); EXPECT_EQ(axis->coord_range(), (Range{-120.0, -20.0})); EXPECT_EQ(axis->label_precision(), 4); EXPECT_TRUE(axis->use_wheel()); EXPECT_TRUE(axis->use_drag()); axis->set_locale({','}); axis->set_coord_start(-100.0); axis->set_coord_length(80.0); EXPECT_EQ(axis->locale(), (Number_Locale{','})); EXPECT_EQ(axis->coord_range(), (Range{-100.0, -20.0})); EXPECT_DOUBLE_EQ(axis->pixel_to_coord(axis->coord_to_pixel(-60.0)), -60.0); EXPECT_GT(axis->pixel_sample_count(), 0); EXPECT_GT(axis->tick_step(axis->coord_range()), 0.0); EXPECT_GE(axis->sub_tick_count(axis->tick_step(axis->coord_range())), 0); const auto time_axis = Time_Axis::Builder(root, Orientation::Horizontal) .set_x(12) .set_y(280) .set_pixel_length(300) .set_visible_time_point_count(32) .set_tick_label_spacing_px(19) .set_time_format("hh:mm:ss.zzz") .set_font({16.0, 700, true}) .set_newest_at_axis_start(true) .build(); ASSERT_TRUE(time_axis); const int first = time_axis->append_time({3'723'004}); const int second = time_axis->append_time({3'724'005}); EXPECT_EQ(time_axis->visible_time_point_count(), 32); EXPECT_EQ(time_axis->tick_label_spacing_px(), 19); EXPECT_EQ(time_axis->time_format(), "hh:mm:ss.zzz"); EXPECT_EQ(time_axis->font(), (Font{16.0, 700, true})); EXPECT_TRUE(time_axis->newest_at_axis_start()); EXPECT_EQ(time_axis->time_point_count(), 2U); EXPECT_EQ(time_axis->tick_to_time(first), (Time_Of_Day{3'723'004})); EXPECT_EQ(time_axis->tick_to_time(second), (Time_Of_Day{3'724'005})); EXPECT_EQ(time_axis->tick_label(first), "01:02:03.004"); } TEST(Renderive_Core2, RetainedSpectrumApisRoundTripAndMarkersRemainObservable) { Plot_Core plot; plot.init(); plot.set_viewport_size({360, 240}); plot.activate_view(); const auto root = plot.root_renderable(); const auto frequency = Frequency_Axis::Builder(root, Orientation::Horizontal) .set_x(30).set_y(210).set_pixel_length(300) .set_coord_range({90.0, 110.0}).build(); const auto power = Axis::Builder(root, Orientation::Vertical) .set_x(30).set_y(20).set_pixel_length(190) .set_coord_range({-20.0, -120.0}).build(); auto spectrum = Spectrum::Builder(root, frequency, power) .set_frequency_range({90.0, 110.0}) .set_frequency_point_size(4) .set_center_frequency(100.0) .set_sweep_frequency_range({96.0, 104.0}) .set_max_hold_visible(true) .set_min_hold_visible(true) .set_max_marker_visible(true) .set_use_min_marker(true) .set_sweep_region_visible(true) .set_visible_range_only(false) .set_interpolation_mode(Line_Interpolation_Mode::Cubic_Value) .build(); ASSERT_TRUE(spectrum); spectrum->set_max_brush({Color{1, 2, 3, 80}, Brush_Style::Solid}); spectrum->set_current_brush({Color{4, 5, 6, 90}, Brush_Style::Solid}); spectrum->set_min_brush({Color{7, 8, 9, 100}, Brush_Style::Solid}); spectrum->set_max_pen({Color{10, 20, 30, 255}, 2.0}); spectrum->set_current_pen({Color{40, 50, 60, 255}, 2.5}); spectrum->set_min_pen({Color{70, 80, 90, 255}, 3.0}); spectrum->set_selected_marker_pen({Color{11, 22, 33, 255}, 4.0}); spectrum->set_marker_pen({Color{44, 55, 66, 255}, 1.5}); spectrum->set_middle_frequency_pen({Color{77, 88, 99, 255}, 1.25}); spectrum->set_sweep_region_brush({Color{12, 34, 56, 70}, Brush_Style::Solid}); EXPECT_EQ(spectrum->frequency_axis(), frequency); EXPECT_EQ(spectrum->power_axis(), power); EXPECT_EQ(spectrum->frequency_point_size(), 4); EXPECT_EQ(spectrum->frequency_range(), (Range{90.0, 110.0})); EXPECT_DOUBLE_EQ(spectrum->center_frequency(), 100.0); EXPECT_EQ(spectrum->sweep_frequency_range(), (Range{96.0, 104.0})); EXPECT_TRUE(spectrum->max_hold_visible()); EXPECT_TRUE(spectrum->min_hold_visible()); EXPECT_TRUE(spectrum->max_marker_visible()); EXPECT_TRUE(spectrum->use_min_marker()); EXPECT_TRUE(spectrum->sweep_region_visible()); EXPECT_FALSE(spectrum->visible_range_only()); EXPECT_EQ(spectrum->interpolation_mode(), Line_Interpolation_Mode::Cubic_Value); EXPECT_EQ(spectrum->max_brush(), (Brush{Color{1, 2, 3, 80}, Brush_Style::Solid})); EXPECT_EQ(spectrum->current_pen(), (Pen{Color{40, 50, 60, 255}, 2.5})); EXPECT_EQ(spectrum->sweep_region_brush(), (Brush{Color{12, 34, 56, 70}, Brush_Style::Solid})); std::pmr::vector samples{std::pmr::get_default_resource()}; samples.assign({-100.0, -80.0, -70.0, -50.0}); spectrum->update_samples(std::move(samples)); EXPECT_EQ(spectrum->sample_count(), 4U); EXPECT_GT(spectrum->rendered_point_count(), 4U); bool valid{}; EXPECT_DOUBLE_EQ(spectrum->power_at(100.0, valid), -75.0); EXPECT_TRUE(valid); EXPECT_DOUBLE_EQ(spectrum->power_at(200.0, valid), 0.0); EXPECT_FALSE(valid); spectrum->add_custom_marker(96.0); spectrum->add_custom_line_marker(102.0); EXPECT_EQ(spectrum->selectable_line_marker_count(), 2); spectrum->select_next_marker(); EXPECT_EQ(spectrum->selected_marker_index(), 0); spectrum->set_current_marker_frequency(97.0); EXPECT_DOUBLE_EQ(spectrum->marker_frequency(0), 97.0); spectrum->select_previous_marker(); EXPECT_EQ(spectrum->selected_marker_index(), 1); spectrum->remove_selected_marker(); EXPECT_EQ(spectrum->selectable_line_marker_count(), 1); spectrum->remove_custom_marker(97.1); EXPECT_EQ(spectrum->selectable_line_marker_count(), 0); spectrum->clear_custom_markers(); EXPECT_EQ(spectrum->selected_marker_index(), -1); EXPECT_TRUE(plot.render_frame(true)); } TEST(Renderive_Core2, RetainedHeatmapSweepAndTraceApisPreserveDataShapes) { Plot_Core plot; plot.init(); plot.set_viewport_size({420, 280}); plot.activate_view(); const auto root = plot.root_renderable(); const auto frequency = Frequency_Axis::Builder(root, Orientation::Horizontal) .set_x(40).set_y(250).set_pixel_length(340) .set_coord_range({0.0, 4.0}).build(); const auto power = Axis::Builder(root, Orientation::Vertical) .set_x(40).set_y(20).set_pixel_length(230) .set_coord_range({0.0, -120.0}).build(); const auto time = Time_Axis::Builder(root, Orientation::Vertical) .set_x(40).set_y(20).set_pixel_length(230) .set_visible_time_point_count(8).build(); auto waterfall = Waterfall::Builder(root, frequency, time) .set_frequency_range({0.0, 4.0}) .set_power_range({-120.0, 0.0}) .set_frequency_bin_count(4) .set_visible_range_only(false) .set_interpolation_mode(Image_Interpolation_Mode::Bicubic) .build(); ASSERT_TRUE(waterfall); EXPECT_EQ(waterfall->frequency_axis(), frequency); EXPECT_EQ(waterfall->time_axis(), time); EXPECT_EQ(waterfall->frequency_range(), (Range{0.0, 4.0})); EXPECT_EQ(waterfall->power_range(), (Range{-120.0, 0.0})); EXPECT_EQ(waterfall->frequency_bin_count(), 4); EXPECT_FALSE(waterfall->visible_range_only()); EXPECT_EQ(waterfall->interpolation_mode(), Image_Interpolation_Mode::Bicubic); const std::array row1{-100.0, -80.0, -60.0, -40.0}; waterfall->append_row(Time_Of_Day{1000}, row1); std::pmr::vector row2{std::pmr::get_default_resource()}; row2.assign({-90.0, -70.0, -50.0, -30.0}); waterfall->append_row(Time_Of_Day{2000}, std::move(row2)); EXPECT_EQ(waterfall->row_count(), 2U); EXPECT_EQ(waterfall->stored_point_count(), 8U); EXPECT_EQ(waterfall->rendered_cell_count(), 8U); auto afterglow = Afterglow::Builder(root, frequency, power) .set_frequency_range({0.0, 4.0}) .set_power_range({-120.0, 0.0}) .set_frequency_bin_count(4) .set_power_bin_count(8) .set_interpolate_power_bins(false) .set_decay_rate(0.35) .build(); ASSERT_TRUE(afterglow); EXPECT_EQ(afterglow->frequency_axis(), frequency); EXPECT_EQ(afterglow->power_axis(), power); EXPECT_EQ(afterglow->frequency_range(), (Range{0.0, 4.0})); EXPECT_EQ(afterglow->power_range(), (Range{-120.0, 0.0})); EXPECT_EQ(afterglow->frequency_point_size(), 4); EXPECT_EQ(afterglow->power_point_size(), 8); EXPECT_FALSE(afterglow->interpolate()); EXPECT_DOUBLE_EQ(afterglow->attenuation_rate(), 0.35); afterglow->append_spectrum(row1); std::pmr::vector glow2{std::pmr::get_default_resource()}; glow2.assign({-95.0, -75.0, -55.0, -35.0}); afterglow->append_spectrum(std::move(glow2)); EXPECT_EQ(afterglow->history_count(), 2U); EXPECT_EQ(afterglow->latest_spectrum_point_count(), 4U); EXPECT_EQ(afterglow->rendered_cell_count(), 32U); afterglow->set_attenuation_rate(2.0); EXPECT_DOUBLE_EQ(afterglow->attenuation_rate(), 1.0); auto sweep = Sweep_Spectrum::Builder(root, frequency, power) .set_frequency_range({0.0, 8.0}) .set_bins_per_block(4) .set_block_num(2) .set_pen({Color{1, 120, 220, 255}, 2.0}) .set_current_frequency_pen({Color{240, 80, 20, 255}, 3.0}) .set_visible_range_only(false) .set_interpolation_mode(Line_Interpolation_Mode::Step_Right) .build(); ASSERT_TRUE(sweep); EXPECT_EQ(sweep->frequency_axis(), frequency); EXPECT_EQ(sweep->power_axis(), power); EXPECT_EQ(sweep->frequency_range(), (Range{0.0, 8.0})); EXPECT_EQ(sweep->bins_per_block(), 4); EXPECT_EQ(sweep->block_count(), 2); EXPECT_FALSE(sweep->visible_range_only()); EXPECT_EQ(sweep->interpolation_mode(), Line_Interpolation_Mode::Step_Right); sweep->append_block(row1); sweep->append_block(row1); std::pmr::vector block3{std::pmr::get_default_resource()}; block3.assign(row1.begin(), row1.end()); sweep->append_block(std::move(block3)); EXPECT_EQ(sweep->stored_block_count(), 2U); EXPECT_EQ(sweep->stored_point_count(), 8U); EXPECT_GT(sweep->rendered_point_count(), 0U); auto trace = Frequency_Trace::Builder(root, time, power) .set_pen({Color{120, 240, 80, 255}, 2.0}) .build(); ASSERT_TRUE(trace); trace->append_sample(Time_Of_Day{3000}, -80.0); trace->append_sample(Time_Of_Day{4000}, -70.0); EXPECT_EQ(trace->time_axis(), time); EXPECT_EQ(trace->value_axis(), power); EXPECT_EQ(trace->sample_count(), 2U); EXPECT_EQ(trace->rendered_point_count(), 2U); EXPECT_TRUE(plot.render_frame(true)); } TEST(Renderive_Core2, RetainedSelectionAndConstellationApisDriveInteractionAndLayout) { Plot_Core plot; plot.init(); plot.set_viewport_size({360, 260}); plot.activate_view(); const auto root = plot.root_renderable(); const auto horizontal = Axis::Builder(root, Orientation::Horizontal) .set_x(30).set_y(230).set_pixel_length(300) .set_coord_range({-3.0, 3.0}).build(); const auto vertical = Axis::Builder(root, Orientation::Vertical) .set_x(30).set_y(20).set_pixel_length(210) .set_coord_range({3.0, -3.0}).build(); auto selection = Selection_Rectangle_Overlay::Builder(root, horizontal, vertical) .set_font({14.0, 600, true}) .set_font_pen({Color{20, 220, 180, 255}, 2.0}) .set_rect_brush({Color{20, 80, 220, 60}, Brush_Style::Solid}) .set_border_pen({Color{240, 200, 60, 255}, 2.0, Line_Style::Dash}) .build(); ASSERT_TRUE(selection); EXPECT_EQ(selection->label_font(), (Font{14.0, 600, true})); EXPECT_EQ(selection->label_pen(), (Pen{Color{20, 220, 180, 255}, 2.0})); EXPECT_EQ(selection->selection_brush(), (Brush{Color{20, 80, 220, 60}, Brush_Style::Solid})); EXPECT_EQ(selection->selection_border_pen(), (Pen{Color{240, 200, 60, 255}, 2.0, Line_Style::Dash})); Pointer_Event press(Event_Type::Pointer_Press); press.position = {80.0, 70.0}; press.button = Mouse_Button::Left; selection->handle_event(press); EXPECT_TRUE(press.is_accepted()); Pointer_Event move(Event_Type::Pointer_Move); move.position = {260.0, 180.0}; selection->handle_event(move); EXPECT_TRUE(move.is_accepted()); Pointer_Event release(Event_Type::Pointer_Release); release.position = {260.0, 180.0}; release.button = Mouse_Button::Left; selection->handle_event(release); EXPECT_TRUE(release.is_accepted()); ASSERT_EQ(selection->selected_regions().size(), 1U); EXPECT_FALSE(selection->selected_regions().front().empty()); selection->clear_selected_regions(); EXPECT_TRUE(selection->selected_regions().empty()); selection->set_horizontal_axis(horizontal); selection->set_vertical_axis(vertical); auto constellation = Constellation_Diagram::Builder(root, horizontal, vertical) .set_i_range({-2.0, 2.0}) .set_q_range({-3.0, 3.0}) .set_point_color({80, 220, 255, 255}) .set_anchor_color({255, 180, 40, 255}) .set_point_lifetime_ms(2000) .set_type(Constellation_Diagram_Type::Psk16) .set_phase_offset_radians(0.25) .build(); ASSERT_TRUE(constellation); EXPECT_EQ(constellation->i_axis(), horizontal); EXPECT_EQ(constellation->q_axis(), vertical); EXPECT_EQ(constellation->i_range(), (Range{-2.0, 2.0})); EXPECT_EQ(constellation->q_range(), (Range{-3.0, 3.0})); EXPECT_EQ(constellation->point_color(), (Color{80, 220, 255, 255})); EXPECT_EQ(constellation->anchor_color(), (Color{255, 180, 40, 255})); EXPECT_EQ(constellation->point_lifetime_ms(), 2000); constellation->append_point({0.5, -0.5}); constellation->append_point({-0.5, 0.5}); EXPECT_EQ(constellation->point_count(), 2U); constellation->fit_square_to_axes(); EXPECT_EQ(horizontal->coord_range(), (Range{-3.0, 3.0})); EXPECT_EQ(vertical->coord_range(), (Range{3.0, -3.0})); EXPECT_TRUE(plot.render_frame(true)); } } // namespace } // namespace renderive