147 lines
7.4 KiB
C++
147 lines
7.4 KiB
C++
#pragma once
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <memory_resource>
|
|
#include <span>
|
|
#include "../Axis/Frequency_Axis.h"
|
|
#include "../Axis/Frequency_Axis_p.h"
|
|
#include "../architecture/Bounded_Input_Buffer.h"
|
|
#include "../base/Memory.h"
|
|
#include "../base/global.h"
|
|
#include "../primitive/Raster_Image_p.h"
|
|
#include "Afterglow.h"
|
|
namespace renderive {
|
|
struct Afterglow_Render_State : Render_State, Afterglow_Prop {};
|
|
struct Afterglow_Input_Data : Input_Data {
|
|
static constexpr std::size_t Max_Pending_Frame_Count = 4;
|
|
Bounded_Vector_Double_Input_Buffer pending_spectra;
|
|
void clear() override {
|
|
pending_spectra.clear();
|
|
}
|
|
};
|
|
struct Afterglow_Private : Typed_Render_Data<Afterglow, Afterglow_Render_State, Afterglow_Input_Data> {
|
|
Raster_Image_Buffer image;
|
|
std::weak_ptr<Color_Bar> color_bar;
|
|
bool image_dirty = false;
|
|
int current_history_index = 0;
|
|
int cached_frequency_bin_count{};
|
|
int cached_power_bin_count{};
|
|
int cached_cell_count{};
|
|
std::uint64_t cache_color_map_hash{};
|
|
std::pmr::vector<double> current_intensity_grid{memory_resource(Memory_Domain::Afterglow)};
|
|
std::pmr::vector<double> previous_intensity_grid{memory_resource(Memory_Domain::Afterglow)};
|
|
std::pmr::vector<double> merged_intensity_grid{memory_resource(Memory_Domain::Afterglow)};
|
|
void update_cache_size(int frequency_point_size, int power_point_size) {
|
|
cached_frequency_bin_count = frequency_point_size;
|
|
cached_power_bin_count = power_point_size;
|
|
cached_cell_count = frequency_point_size * power_point_size;
|
|
current_intensity_grid.resize(cached_cell_count);
|
|
std::fill(current_intensity_grid.begin(), current_intensity_grid.end(), 0.0);
|
|
previous_intensity_grid.resize(cached_cell_count);
|
|
std::fill(previous_intensity_grid.begin(), previous_intensity_grid.end(), 0.0);
|
|
merged_intensity_grid.resize(cached_cell_count);
|
|
std::fill(merged_intensity_grid.begin(), merged_intensity_grid.end(), 0.0);
|
|
current_history_index = 0;
|
|
}
|
|
void push_data(std::span<const double> power_range_data, const Afterglow_Render_State* s, const Color_Scale_Snapshot& color_scale);
|
|
void draw_image(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Afterglow_Render_State* s, const Color_Scale_Snapshot& color_scale) {
|
|
auto frequency_axis = s->frequency_axis.lock();
|
|
auto power_axis = s->power_axis.lock();
|
|
if (!frequency_axis || !power_axis || image.is_null())
|
|
return;
|
|
if (s->frequency_range.length() == 0.0 || color_scale.range.length() == 0.0)
|
|
return;
|
|
Axis_Mapping_2D mapping = Axis_Render_Access::mapping(frequency_axis.get(), power_axis.get(), snapshot);
|
|
Axis_Basis_2D basis = Axis_Basis_2D::from_ranges(mapping, s->frequency_range, color_scale.range);
|
|
canvas.save();
|
|
canvas.transform(
|
|
basis.domain_vector.x / static_cast<double>(image.width()),
|
|
basis.domain_vector.y / static_cast<double>(image.width()),
|
|
basis.value_vector.x / static_cast<double>(image.height()),
|
|
basis.value_vector.y / static_cast<double>(image.height()),
|
|
basis.origin.x,
|
|
basis.origin.y);
|
|
image.draw(canvas, RectF{0.0, 0.0, static_cast<double>(image.width()), static_cast<double>(image.height())});
|
|
canvas.restore();
|
|
}
|
|
void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override {
|
|
Afterglow_Render_State* s = render_state(frame_view);
|
|
if (!s)
|
|
return;
|
|
auto color_scale = s->color_scale.snapshot();
|
|
draw_image(canvas, snapshot, s, *color_scale);
|
|
}
|
|
void prepare_data(const Render_Frame_Snapshot&, const Renderable_Frame_View& frame_view) override {
|
|
Afterglow_Render_State* s = render_state(frame_view);
|
|
Afterglow_Input_Data* input = render_input_data(frame_view);
|
|
if (!s || !input)
|
|
return;
|
|
if (s->frequency_bin_count <= 0 || s->power_bin_count <= 0)
|
|
return;
|
|
auto color_scale = s->color_scale.snapshot();
|
|
if (cached_frequency_bin_count != s->frequency_bin_count || cached_power_bin_count != s->power_bin_count) {
|
|
update_cache_size(s->frequency_bin_count, s->power_bin_count);
|
|
}
|
|
bool image_size_changed = image.width() != s->frequency_bin_count || image.height() != s->power_bin_count;
|
|
std::uint64_t color_map_hash = color_scale->color_map.hash();
|
|
if (cache_color_map_hash != color_map_hash) {
|
|
cache_color_map_hash = color_map_hash;
|
|
image_dirty = true;
|
|
}
|
|
if (image_size_changed) {
|
|
image.resize(s->frequency_bin_count, s->power_bin_count);
|
|
image.fill(Color::transparent());
|
|
image_dirty = true;
|
|
}
|
|
input->pending_spectra.read_all([this, s, color_scale](const auto& data) {
|
|
if (data.size() != s->frequency_bin_count) {
|
|
return;
|
|
}
|
|
push_data(data, s, *color_scale);
|
|
});
|
|
if (image_dirty) {
|
|
image.write_normalized_grid(previous_intensity_grid.data(), s->frequency_bin_count, s->power_bin_count, color_scale->color_map);
|
|
image_dirty = false;
|
|
}
|
|
frame_view.consume_input();
|
|
}
|
|
};
|
|
template <typename That>
|
|
void Afterglow::Builder_T<That>::init(std::shared_ptr<Renderable> parent, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Axis> power_axis) {
|
|
ASSERT(parent && parent->attached_to_plot(), "Afterglow build error! parent invalid");
|
|
ASSERT(frequency_axis && power_axis, "Afterglow build error! axis == nullptr");
|
|
ASSERT(frequency_axis->attached_to_plot(), "Afterglow build error! frequency_axis->plot == nullptr");
|
|
ASSERT(frequency_axis->shares_plot_with(*power_axis), "Afterglow build error! frequency_axis->plot != power_axis->plot");
|
|
ASSERT(parent->shares_plot_with(*frequency_axis), "Afterglow build error! parent->plot != frequency_axis->plot");
|
|
ASSERT(frequency_axis->orientation() != power_axis->orientation(), "Afterglow build error! axes must be orthogonal");
|
|
this->parent = parent;
|
|
static_cast<That*>(this)->frequency_axis = frequency_axis;
|
|
static_cast<That*>(this)->power_axis = power_axis;
|
|
}
|
|
template <typename That>
|
|
void Afterglow::Builder_T<That>::set(Afterglow* ret) {
|
|
ret->init(parent);
|
|
std::shared_ptr<Renderable> owner = ret->shared_from_this();
|
|
Afterglow_Private* pd = ret->d();
|
|
Render_Edit_Lease<Afterglow_Private, Afterglow_Render_State> edit(pd);
|
|
Afterglow_Render_State* sc = edit.operator->();
|
|
PROP_RT(std::weak_ptr<Frequency_Axis>, frequency_axis)
|
|
PROP_RT(std::weak_ptr<Axis>, power_axis)
|
|
PROP_RT(Range, frequency_range)
|
|
PROP_RT(int, frequency_bin_count)
|
|
PROP_RT(int, power_bin_count)
|
|
PROP_RT(bool, interpolate_power_bins)
|
|
PROP_RT(double, decay_rate)
|
|
PROP_RT(int, initial_intensity_threshold)
|
|
sc->color_scale = static_cast<That*>(this)->color_scale.clone();
|
|
sc->frequency_bin_count = std::max(1, sc->frequency_bin_count);
|
|
sc->power_bin_count = std::max(1, sc->power_bin_count);
|
|
sc->decay_rate = std::clamp(sc->decay_rate, 0.0, 1.0);
|
|
sc->initial_intensity_threshold = std::max(1, sc->initial_intensity_threshold);
|
|
if (static_cast<That*>(this)->color_bar_enabled) {
|
|
auto bar = Color_Bar::Builder(owner).set_color_scale(sc->color_scale).set_name("Afterglow").build();
|
|
pd->color_bar = bar;
|
|
}
|
|
}
|
|
} // namespace renderive
|