Files
Renderive/render_2D/render_2D/plottable/Sweep_Spectrum.cpp
T
2026-08-18 11:09:48 +08:00

153 lines
7.2 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 <cstdint>
#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::size_t current_sweep_block_count(std::uint64_t total_update_count,
std::size_t block_count) {
if (total_update_count == 0) return 0;
return static_cast<std::size_t>((total_update_count - 1) % block_count) + 1;
}
std::vector<double> flatten_latest(const std::deque<std::vector<double>>& blocks,
std::size_t block_count) {
std::vector<double> values;
const std::size_t first = blocks.size() - block_count;
std::size_t value_count{};
for (std::size_t index = first; index < blocks.size(); ++index) {
value_count += blocks[index].size();
}
values.reserve(value_count);
for (std::size_t index = first; index < blocks.size(); ++index) {
values.insert(values.end(), blocks[index].begin(), blocks[index].end());
}
return values;
}
Range completed_frequency_range(Range frequency_range,
std::size_t completed_block_count,
std::size_t block_count) {
const double completed = static_cast<double>(completed_block_count) / static_cast<double>(block_count);
return {frequency_range.origin, frequency_range.origin + frequency_range.length() * completed};
}
}
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(renderive_Owner<Axis> frequency_axis,
renderive_Owner<Axis> power_axis)
: Renderable(With_Attached_Impl<Impl, State>{}, frequency_axis, power_axis) {
add_build_dependency_parent(frequency_axis);
add_build_dependency_parent(power_axis);
add_build_display_child(frequency_axis);
add_build_display_child(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 snapshot = d_func<Impl>().blocks->snapshot_with_update_state();
const std::size_t block_count = static_cast<std::size_t>(state.block_count.get());
const std::size_t completed_block_count =
current_sweep_block_count(snapshot.update_state.total_update_count, block_count);
const auto values = flatten_latest(snapshot.values, completed_block_count);
const Range frequency_range = completed_frequency_range(
state.frequency_range, completed_block_count, block_count);
return curve_points(values, 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& published_update_state = view.update_state(*blocks);
const std::size_t block_count = static_cast<std::size_t>(state.block_count.get());
const std::size_t completed_block_count = current_sweep_block_count(
published_update_state.total_update_count, block_count);
const auto values = flatten_latest(published_blocks, completed_block_count);
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);
const Range frequency_range = completed_frequency_range(
state.frequency_range, completed_block_count, block_count);
output.points = curve_points(values, frequency_range, x, y,
state.visible_range_only, state.interpolation_mode);
const double frequency = frequency_range.target;
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);
}
}