#pragma once #include "../Axis/Axis_p.h" #include "../architecture/Bounded_Input_Buffer.h" #include "../base/Frame_Memory.h" #include "../base/Geometry.h" #include "../base/Style.h" #include "../event/Pointer_Event.h" #include "../render/Canvas.h" #include "../primitive/Curve.h" #include "../primitive/Curve_Utils_p.h" #include "Sweep_Spectrum.h" namespace renderive { struct Sweep_Spectrum_Render_State : renderive::Render_State, Sweep_Spectrum_Prop {}; struct Sweep_Spectrum_Input_Data : renderive::Input_Data { static constexpr std::size_t Latest_Only_Count = 1; Bounded_Vector_Double_Input_Buffer data; void clear() override { data.clear(); } }; struct Sweep_Spectrum_Private : renderive::Typed_Render_Data { std::vector sweep_points; int next_block_index{}; int bins_per_block{}; int block_count{}; Range frequency_range; bool first_sweep_incomplete = true; void reset_sweep_grid(int block_frequency_point_size, int block_num, Range frequency_range) { this->bins_per_block = block_frequency_point_size; this->block_count = block_num; this->frequency_range = frequency_range; int total_point_count = block_num * block_frequency_point_size; if (total_point_count <= 0) { sweep_points.clear(); first_sweep_incomplete = true; return; } sweep_points.resize(total_point_count); int interval_count = total_point_count - 1; if (interval_count == 0) { sweep_points[0].x = frequency_range.origin; first_sweep_incomplete = true; return; } double step = frequency_range.length() / interval_count; double frequency = frequency_range.origin; for (int i = 0; i < interval_count; ++i) { sweep_points[i].x = frequency; frequency += step; } sweep_points.back().x = frequency_range.target; first_sweep_incomplete = true; } void prepare_data(const Render_Frame_Snapshot&, const Renderable_Frame_View& frame_view) override { Sweep_Spectrum_Render_State* s = render_state(frame_view); Sweep_Spectrum_Input_Data* d = render_input_data(frame_view); if (!s || !d) return; if ( bins_per_block != s->bins_per_block || block_count != s->block_num || frequency_range != s->frequency_range) { reset_sweep_grid(s->bins_per_block, s->block_num, s->frequency_range); next_block_index = 0; } d->data.read_all([this, d](const auto& data) { if ((int)data.size() != bins_per_block || sweep_points.empty()) { return; } PointF* start = &sweep_points[next_block_index * bins_per_block]; for (int i = 0; i < bins_per_block; ++i) { start[i].y = data[i]; } next_block_index++; if (next_block_index == block_count) { next_block_index = 0; first_sweep_incomplete = false; } }); frame_view.consume_input(); } void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { Sweep_Spectrum_Render_State* s = render_state(frame_view); if (!s) return; auto frequency_axis = s->frequency_axis.lock(); auto power_axis = s->power_axis.lock(); if (!frequency_axis || !power_axis) return; Axis_Mapping_2D mapping = Axis_Render_Access::mapping(frequency_axis.get(), power_axis.get(), snapshot); int total_point_count = (int)sweep_points.size(); int available_count = !first_sweep_incomplete ? total_point_count : next_block_index * bins_per_block; std::pmr::vector data(frame_memory_resource()); data = Curve_Utils::build_resampled_points(mapping, s->frequency_range, total_point_count, available_count, s->visible_range_only, s->interpolation_mode, [this](int i) { return sweep_points.at(i).y; }); Curve_Utils::draw_polyline(&canvas, data, s->pen); canvas.set_pen(s->current_frequency_pen); double current_frequency = block_count > 1 ? frequency_range.origin + next_block_index * frequency_range.length() / (block_count - 1.0) : frequency_range.origin; canvas.draw_line( mapping.map(current_frequency, mapping.value.coord_range.origin), mapping.map(current_frequency, mapping.value.coord_range.target)); } }; } // namespace renderive