#include "Sweep_Spectrum.h" #include "Curve_Sampling.h" #include "../render/Blend2D_Cache.h" #include #include #include namespace renderive { namespace detail { namespace { struct Sweep_Spectrum_Runtime_Base {}; struct Sweep_Spectrum_Runtime { std::deque> blocks; }; using Sweep_Spectrum_Runtime_State = Double_State_Strategy; RectF axis_content_rect(const Axis_Transform& horizontal, const Axis_Transform& vertical) { const double x1 = horizontal.coord_to_pixel(horizontal.coordinate_range.origin); const double x2 = horizontal.coord_to_pixel(horizontal.coordinate_range.target); const double y1 = vertical.coord_to_pixel(vertical.coordinate_range.origin); const double y2 = vertical.coord_to_pixel(vertical.coordinate_range.target); return {std::min(x1, x2), std::min(y1, y2), std::abs(x2 - x1), std::abs(y2 - y1)}; } std::vector flatten(const Sweep_Spectrum_Runtime& runtime) { std::vector values; for(const auto& block : runtime.blocks) values.insert(values.end(), block.begin(), block.end()); return values; } } struct Sweep_Spectrum_Control::Impl { Impl(std::shared_ptr frequency, std::shared_ptr power) : frequency_axis(std::move(frequency)), power_axis(std::move(power)) {} std::shared_ptr frequency_axis; std::shared_ptr power_axis; Sweep_Spectrum_Runtime_State runtime; }; Sweep_Spectrum_Control::Sweep_Spectrum_Control(Plot_Core& plot, const Sweep_Spectrum_Properties& properties, std::shared_ptr frequency_axis, std::shared_ptr power_axis) : Plottable_State(plot, properties), impl_(std::make_unique(std::move(frequency_axis), std::move(power_axis))) {} Sweep_Spectrum_Control::~Sweep_Spectrum_Control() = default; void Sweep_Spectrum_Control::append_block(std::span values) { if(get<&Sweep_Spectrum_Properties::bins_per_block>() <= 0) set<&Sweep_Spectrum_Properties::bins_per_block>(static_cast(values.size())); const int limit = get<&Sweep_Spectrum_Properties::block_count>(); impl_->runtime.update([values, limit](Sweep_Spectrum_Runtime& runtime) { runtime.blocks.emplace_back(values.begin(), values.end()); while(runtime.blocks.size() > static_cast(limit)) runtime.blocks.pop_front(); }); changed(); } void Sweep_Spectrum_Control::append_block(std::pmr::vector&& values) { append_block(std::span(values.data(), values.size())); } std::size_t Sweep_Spectrum_Control::stored_block_count() const { return impl_->runtime.read([](const Sweep_Spectrum_Runtime& runtime) { return runtime.blocks.size(); }); } std::size_t Sweep_Spectrum_Control::stored_point_count() const { return impl_->runtime.read([](const Sweep_Spectrum_Runtime& runtime) { std::size_t count{}; for(const auto& block : runtime.blocks) count += block.size(); return count; }); } std::size_t Sweep_Spectrum_Control::rendered_point_count() const { const auto state = properties(); const auto runtime = impl_->runtime.read([](const Sweep_Spectrum_Runtime& value) { return value; }); const auto values = flatten(runtime); return curve_points(values, state.frequency_range, impl_->frequency_axis->transform(), impl_->power_axis->transform(), state.visible_range_only, state.interpolation_mode).size(); } void Sweep_Spectrum_Control::publish() { publish_properties(); const int limit = get<&Sweep_Spectrum_Properties::block_count>(); impl_->runtime.update([limit](Sweep_Spectrum_Runtime& runtime) { while(runtime.blocks.size() > static_cast(limit)) runtime.blocks.pop_front(); }); impl_->runtime.publish(); } void Sweep_Spectrum_Control::paint(Painter& painter) { const auto state = render_properties(); const auto runtime = impl_->runtime.render_use_state(); const auto values = flatten(runtime); if(values.size() < 2) return; const Axis_Transform x = impl_->frequency_axis->transform(); const Axis_Transform y = impl_->power_axis->transform(); painter.polyline(curve_points(values, state.frequency_range, x, y, state.visible_range_only, state.interpolation_mode), state.pen); const double completed = std::min(1.0, static_cast(runtime.blocks.size()) / state.block_count.get()); const double frequency = state.frequency_range.origin + state.frequency_range.length() * completed; const RectF content = axis_content_rect(x, y); const double marker_x = x.coord_to_pixel(frequency); painter.line({marker_x, content.y}, {marker_x, content.bottom()}, state.current_frequency_pen); } } }