#include #include "Afterglow_p.h" #include "../base/Frame_Memory.h" #include "../base/Memory.h" namespace renderive { RENDERIVE_DEFINE_RENDERABLE_BINDING(Afterglow, Afterglow_Private, Afterglow_Render_State, Afterglow_Input_Data, "Afterglow") std::shared_ptr Afterglow::frequency_axis() { return d()->edit_state_value(&Afterglow_Render_State::frequency_axis).lock(); } void Afterglow::set_frequency_axis(std::shared_ptr value) { d()->set_state_value(&Afterglow_Render_State::frequency_axis, std::weak_ptr(value)); } std::shared_ptr Afterglow::power_axis() { return d()->edit_state_value(&Afterglow_Render_State::power_axis).lock(); } void Afterglow::set_power_axis(std::shared_ptr value) { d()->set_state_value(&Afterglow_Render_State::power_axis, std::weak_ptr(value)); } Range Afterglow::frequency_range() { return d()->edit_state_value(&Afterglow_Render_State::frequency_range); } void Afterglow::set_frequency_range(Range value) { if (value.length() == 0.0) return; d()->set_state_value(&Afterglow_Render_State::frequency_range, std::move(value)); } Range Afterglow::power_range() { return d()->edit_state_value(&Afterglow_Render_State::color_scale).range(); } void Afterglow::set_power_range(Range value) { if (value.size() == 0.0) return; Render_Edit_Lease edit(d()); edit->color_scale.set_range(value); } int Afterglow::frequency_point_size() { return d()->edit_state_value(&Afterglow_Render_State::frequency_bin_count); } int Afterglow::power_point_size() { return d()->edit_state_value(&Afterglow_Render_State::power_bin_count); } bool Afterglow::interpolate() { return d()->edit_state_value(&Afterglow_Render_State::interpolate_power_bins); } void Afterglow::set_interpolate(bool value) { d()->set_state_value(&Afterglow_Render_State::interpolate_power_bins, value); } double Afterglow::attenuation_rate() { return d()->edit_state_value(&Afterglow_Render_State::decay_rate); } void Afterglow::set_attenuation_rate(double value) { d()->set_state_value(&Afterglow_Render_State::decay_rate, std::clamp(value, 0.0, 1.0)); } int Afterglow::init_strong_value() { return d()->edit_state_value(&Afterglow_Render_State::initial_intensity_threshold); } void Afterglow::set_init_strong_value(int value) { d()->set_state_value(&Afterglow_Render_State::initial_intensity_threshold, std::max(1, value)); } Color_Map Afterglow::color_map() { return d()->edit_state_value(&Afterglow_Render_State::color_scale).color_map(); } void Afterglow::set_color_map(Color_Map value) { Render_Edit_Lease edit(d()); edit->color_scale.set_color_map(std::move(value)); } std::shared_ptr Afterglow::color_bar() const { return const_cast(this)->d()->color_bar.lock(); } void Afterglow::set_frequency_point_size(int t) { Afterglow_Private* pd = d(); Render_Edit_Lease edit(pd); edit->frequency_bin_count = std::max(1, t); edit->power_bin_count = std::max(1, edit->power_bin_count); } void Afterglow::set_power_point_size(int t) { Afterglow_Private* pd = d(); Render_Edit_Lease edit(pd); edit->frequency_bin_count = std::max(1, edit->frequency_bin_count); edit->power_bin_count = std::max(1, t); } Afterglow::Builder::Builder(std::shared_ptr parent, std::shared_ptr frequency_axis, std::shared_ptr power_axis) { init(parent, frequency_axis, power_axis); } std::shared_ptr Afterglow::Builder::build() { auto ret = renderive::make_shared(); set(ret.get()); return ret; } void Afterglow::append_spectrum(std::span power_range_data) { if (!ok()) return; Afterglow_Private* pd = d(); int frequency_point_size{}; { Render_State_Lease state(pd); frequency_point_size = state->frequency_bin_count; } if (power_range_data.size() != frequency_point_size) return; Render_Input_Lease input(pd); input->pending_spectra.push(power_range_data, Afterglow_Input_Data::Max_Pending_Frame_Count); } void Afterglow::append_spectrum(std::pmr::vector&& power_range_data) { if (!ok()) return; Afterglow_Private* pd = d(); int frequency_point_size{}; { Render_State_Lease state(pd); frequency_point_size = state->frequency_bin_count; } if (power_range_data.size() != frequency_point_size) return; Render_Input_Lease input(pd); input->pending_spectra.push(std::move(power_range_data), Afterglow_Input_Data::Max_Pending_Frame_Count); } void Afterglow_Private::push_data(std::span power_range_data, const Afterglow_Render_State* s, const Color_Scale_Snapshot& color_scale) { if (power_range_data.size() != s->frequency_bin_count || s->initial_intensity_threshold <= 0 || color_scale.range.size() == 0.0 || s->frequency_bin_count <= 0 || s->power_bin_count <= 0) return; std::pmr::vector index_list(s->frequency_bin_count, frame_memory_resource()); double rate = (double)s->power_bin_count / color_scale.range.size(); for (int i = 0; i < s->frequency_bin_count; ++i) { int line_index = (int)((power_range_data[i] - color_scale.range.origin) * rate); if (line_index < 0) line_index = 0; if (line_index > s->power_bin_count - 1) line_index = s->power_bin_count - 1; index_list[i] = line_index; } if (!s->interpolate_power_bins) { for (int i = 0; i < s->frequency_bin_count; ++i) { int index = index_list[i] * s->frequency_bin_count + i; current_intensity_grid[index]++; } } else { int last_lower = -1, last_upper = -1; for (int i = 0; i < s->frequency_bin_count; ++i) { int y = index_list[i]; current_intensity_grid[y * s->frequency_bin_count + i]++; if (y - last_upper >= 2) { for (int cur_y = last_upper + 1; cur_y < y; ++cur_y) { current_intensity_grid[cur_y * s->frequency_bin_count + i]++; } last_upper = y; last_lower = last_lower + 1; continue; } if (last_lower - y >= 2) { for (int cur_y = y + 1; cur_y < last_lower; ++cur_y) { current_intensity_grid[cur_y * s->frequency_bin_count + i]++; } last_upper = last_lower - 1; last_lower = y; continue; } last_upper = y; last_lower = y; } } current_history_index++; if (current_history_index < s->initial_intensity_threshold) return; current_history_index = 0; double old_remain = s->decay_rate == 1.0 ? 1.0 : 1.0 - s->decay_rate; double cache_remain = s->decay_rate / (double)s->initial_intensity_threshold; for (int i = 0; i < cached_cell_count; ++i) { merged_intensity_grid[i] = old_remain * previous_intensity_grid[i] + cache_remain * current_intensity_grid[i]; current_intensity_grid[i] = 0; } previous_intensity_grid = merged_intensity_grid; image_dirty = true; } } // namespace renderive