117 lines
5.4 KiB
C++
117 lines
5.4 KiB
C++
#include "Sweep_Spectrum.h"
|
|
#include "Curve_Sampling.h"
|
|
#include "Plottable_Real_Time_Data.h"
|
|
#include "../render/Blend2D_Cache.h"
|
|
#include "../renderable/Renderable_p.h"
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <deque>
|
|
#include <optional>
|
|
namespace renderive::detail {
|
|
namespace {
|
|
using Sweep_Spectrum_History = Plottable_History_Real_Time_Data<std::vector<double>, std::deque<std::vector<double>>>;
|
|
std::vector<double> flatten(const std::deque<std::vector<double>>& blocks) {
|
|
std::vector<double> values;
|
|
for (const auto& block : blocks) values.insert(values.end(), block.begin(), block.end());
|
|
return values;
|
|
}
|
|
}
|
|
struct Sweep_Spectrum::Impl
|
|
: Base::next_Impl<Impl> {
|
|
using Observer = Sweep_Spectrum::Observer;
|
|
friend struct Sweep_Spectrum::Observer;
|
|
struct Prepare_Buffer {
|
|
std::vector<PointF> points;
|
|
PointF current_first;
|
|
PointF current_second;
|
|
bool valid{};
|
|
};
|
|
Impl(renderive_Owner<Axis> frequency, renderive_Owner<Axis> power) : frequency_axis(std::move(frequency)), power_axis(std::move(power)) {}
|
|
renderive_Owner<Axis> frequency_axis;
|
|
renderive_Owner<Axis> power_axis;
|
|
std::optional<Sweep_Spectrum_History> blocks;
|
|
Prepare_Buffer prepare_buffer;
|
|
void prepare_frame(const Prepare_Render_Context& context) override;
|
|
void paint(Painter& painter, const Paint_Render_Context& context) override;
|
|
void handle_observation(const Renderable_Event_View& observation);
|
|
};
|
|
void Sweep_Spectrum::Impl::handle_observation(
|
|
const Renderable_Event_View& observation) {
|
|
if (observation.event != Renderable_Observer_Event::Published) return;
|
|
auto& control = static_cast<Sweep_Spectrum&>(owner());
|
|
const int limit = control.get_state<State, &State::block_count>();
|
|
blocks->retain_latest(static_cast<std::size_t>(limit));
|
|
}
|
|
Sweep_Spectrum::Sweep_Spectrum(const State& state,
|
|
renderive_Owner<Axis> frequency_axis,
|
|
renderive_Owner<Axis> power_axis) : Renderable(With_Attached_Impl<Impl>{}, state,
|
|
std::move(frequency_axis), std::move(power_axis)) {
|
|
d_func<Impl>().blocks.emplace(*this);
|
|
}
|
|
Sweep_Spectrum::~Sweep_Spectrum() = default;
|
|
void Sweep_Spectrum::append_block(std::span<const double> values) {
|
|
if (get_state<State, &State::bins_per_block>() <= 0)
|
|
set_state<State, &State::bins_per_block>(
|
|
static_cast<int>(values.size()));
|
|
const int limit = get_state<State, &State::block_count>();
|
|
d_func<Impl>().blocks->update({values.begin(), values.end()}, static_cast<std::size_t>(limit));
|
|
d_func().changed();
|
|
}
|
|
void Sweep_Spectrum::append_block(std::pmr::vector<double>&& values) {
|
|
append_block(std::span<const double>(values.data(), values.size()));
|
|
}
|
|
std::size_t Sweep_Spectrum::stored_block_count() const {
|
|
return d_func<Impl>().blocks->size();
|
|
}
|
|
std::size_t Sweep_Spectrum::stored_point_count() const {
|
|
const auto blocks = d_func<Impl>().blocks->snapshot();
|
|
std::size_t count{};
|
|
for (const auto& block : blocks) count += block.size();
|
|
return count;
|
|
}
|
|
std::size_t Sweep_Spectrum::rendered_point_count() const {
|
|
const auto state = Renderable::state<State>();
|
|
const auto values = flatten(d_func<Impl>().blocks->snapshot());
|
|
return curve_points(values, state.frequency_range, d_func<Impl>().frequency_axis->transform(), d_func<Impl>().power_axis->transform(), state.visible_range_only, state.interpolation_mode).size();
|
|
}
|
|
Sweep_Spectrum::Observer Sweep_Spectrum::capture_observation() const {
|
|
const auto value = Renderable::published_state<State>();
|
|
Observer result;
|
|
result.state = value;
|
|
result.stored_block_count = stored_block_count();
|
|
result.stored_point_count = stored_point_count();
|
|
result.rendered_point_count = rendered_point_count();
|
|
return result;
|
|
}
|
|
void Sweep_Spectrum::Impl::prepare_frame(const Prepare_Render_Context& context) {
|
|
auto& control = static_cast<Sweep_Spectrum&>(owner());
|
|
const auto& view = context.frame.render_state;
|
|
const auto& state = control.render_state<State>(view);
|
|
const auto& published_blocks = view.get(*blocks);
|
|
const auto values = flatten(published_blocks);
|
|
auto& output = prepare_buffer;
|
|
output = {};
|
|
if (values.size() < 2) return;
|
|
const Axis_Transform x = frequency_axis->transform(view);
|
|
const Axis_Transform y = power_axis->transform(view);
|
|
output.points = curve_points(values, state.frequency_range, x, y,
|
|
state.visible_range_only, state.interpolation_mode);
|
|
const double completed = std::min(1.0, static_cast<double>(published_blocks.size()) / state.block_count.get());
|
|
const double frequency = state.frequency_range.origin + state.frequency_range.length() * completed;
|
|
output.current_first = mapped_point(x, frequency, y, y.coordinate_range.origin);
|
|
output.current_second = mapped_point(x, frequency, y, y.coordinate_range.target);
|
|
output.valid = true;
|
|
}
|
|
void Sweep_Spectrum::Impl::paint(Painter& painter,
|
|
const Paint_Render_Context& context) {
|
|
auto& control = static_cast<Sweep_Spectrum&>(owner());
|
|
const auto& output = prepare_buffer;
|
|
if (!output.valid) return;
|
|
const auto& view = context.frame.render_state;
|
|
const auto& state = control.render_state<State>(view);
|
|
painter.polyline(output.points, state.pen);
|
|
painter.line(output.current_first, output.current_second,
|
|
state.current_frequency_pen);
|
|
}
|
|
}
|