Files
Renderive/Core/plottable/Spectrum_p.h
T
2026-08-02 18:58:35 +08:00

224 lines
9.7 KiB
C++

#pragma once
#include <atomic>
#include <array>
#include <cmath>
#include <memory_resource>
#include <span>
#include "../Axis/Frequency_Axis_p.h"
#include "../Axis/Axis_p.h"
#include "../architecture/Renderable.h"
#include "../base/Frame_Memory.h"
#include "../base/Memory.h"
#include "../base/Color.h"
#include "../base/Geometry.h"
#include "../base/Style.h"
#include "../render/Canvas.h"
#include "Hover_Tooltip.h"
#include "Interpolation_p.h"
#include "../primitive/Label.h"
#include "../primitive/Marker.h"
#include "Spectrum.h"
namespace renderive {
struct Spectrum_Marker {
Spectrum_Marker() = default;
Spectrum_Marker(double frequency, bool is_line_marker) : frequency(frequency), is_line_marker(is_line_marker) {}
double frequency{};
bool is_line_marker = true;
bool operator==(const Spectrum_Marker& other) const {
return std::abs(frequency - other.frequency) < 1e-12 && is_line_marker == other.is_line_marker;
}
};
struct Spectrum_Input_Data : Latest_Input_Data {
std::pmr::vector<double> cur_data{memory_resource(Memory_Domain::Spectrum)};
std::pmr::vector<double> max_data{memory_resource(Memory_Domain::Spectrum)};
std::pmr::vector<double> min_data{memory_resource(Memory_Domain::Spectrum)};
bool has_data = false;
bool has_max_data = false;
bool has_min_data = false;
void push(std::span<const double> data, bool use_max, bool use_min) {
cur_data.assign(data.begin(), data.end());
has_data = true;
if (use_max) {
if (!has_max_data || max_data.size() != data.size()) {
max_data.assign(data.begin(), data.end());
has_max_data = true;
}
else {
for (size_t i = 0; i < data.size(); ++i) {
if (data[i] > max_data[i])
max_data[i] = data[i];
}
}
}
if (use_min) {
if (!has_min_data || min_data.size() != data.size()) {
min_data.assign(data.begin(), data.end());
has_min_data = true;
}
else {
for (size_t i = 0; i < data.size(); ++i) {
if (data[i] < min_data[i])
min_data[i] = data[i];
}
}
}
mark_latest_pushed();
}
void push(std::pmr::vector<double>&& data, bool use_max, bool use_min) {
cur_data = std::move(data);
has_data = true;
if (use_max) {
if (!has_max_data || max_data.size() != cur_data.size()) {
max_data.assign(cur_data.begin(), cur_data.end());
has_max_data = true;
}
else {
for (size_t i = 0; i < cur_data.size(); ++i) {
if (cur_data[i] > max_data[i])
max_data[i] = cur_data[i];
}
}
}
if (use_min) {
if (!has_min_data || min_data.size() != cur_data.size()) {
min_data.assign(cur_data.begin(), cur_data.end());
has_min_data = true;
}
else {
for (size_t i = 0; i < cur_data.size(); ++i) {
if (cur_data[i] < min_data[i])
min_data[i] = cur_data[i];
}
}
}
mark_latest_pushed();
}
template <typename Handler>
void read_all(Handler handler) {
if (!has_data)
return;
handler(cur_data, has_max_data ? &max_data : nullptr, has_min_data ? &min_data : nullptr);
}
void clear() override {
cur_data.clear();
max_data.clear();
min_data.clear();
has_data = false;
has_max_data = false;
has_min_data = false;
clear_latest();
}
};
struct Spectrum_Render_State : Render_State, Hover_Tooltip_State, Spectrum_Prop {
std::pmr::vector<Spectrum_Marker> marker_list{memory_resource(Memory_Domain::Spectrum)};
int cur_select_index = -1;
void un_select() {
cur_select_index = -1;
}
void select_next_marker() {
if (marker_list.empty())
return;
cur_select_index = (cur_select_index + 1) % static_cast<int>(marker_list.size());
}
void select_prev_marker() {
if (marker_list.empty())
return;
cur_select_index = (cur_select_index - 1 + static_cast<int>(marker_list.size())) % static_cast<int>(marker_list.size());
}
void set_select_index(int index) {
if (index < 0 || index >= static_cast<int>(marker_list.size()))
return;
cur_select_index = index;
}
void set_cur_frequency(double frequency) {
set_frequency(cur_select_index, frequency);
}
void set_frequency(int index, double frequency) {
if (index < 0 || index >= static_cast<int>(marker_list.size()))
return;
marker_list[index].frequency = frequency;
}
void draw_axis_rect(Canvas& canvas, const Render_Frame_Snapshot& snapshot, Range range, double middle_x) const {
auto frequency_axis = this->frequency_axis.lock();
auto power_axis = this->power_axis.lock();
if (!frequency_axis || !power_axis)
return;
Axis_Mapping_2D mapping = Axis_Render_Access::mapping(frequency_axis.get(), power_axis.get(), snapshot);
canvas.save();
canvas.set_pen(Pen{.style = Line_Style::None});
canvas.set_brush(sweep_region_brush);
std::array<PointF, 4> rect_points{
mapping.map(range.origin, mapping.value.coord_range.origin),
mapping.map(range.target, mapping.value.coord_range.origin),
mapping.map(range.target, mapping.value.coord_range.target),
mapping.map(range.origin, mapping.value.coord_range.target)
};
canvas.draw_polygon(std::span<const PointF>(rect_points.data(), rect_points.size()), true);
canvas.set_pen(middle_frequency_pen);
canvas.draw_line(
mapping.map(middle_x, mapping.value.coord_range.origin),
mapping.map(middle_x, mapping.value.coord_range.target));
canvas.restore();
}
};
struct Spectrum_Private : Typed_Render_Data<Spectrum, Spectrum_Render_State, Spectrum_Input_Data>, Hit_Testable, Hover_Interactive {
double min_marker_key{};
double min_marker_value{};
double max_marker_key{};
double max_marker_value{};
int cache_frequency_point_size{};
Range cache_frequency_range{};
bool has_power_data = false;
std::pmr::vector<double> frequency{memory_resource(Memory_Domain::Spectrum)};
std::pmr::vector<double> max_powers{memory_resource(Memory_Domain::Spectrum)};
std::pmr::vector<double> cur_powers{memory_resource(Memory_Domain::Spectrum)};
std::pmr::vector<double> min_powers{memory_resource(Memory_Domain::Spectrum)};
std::pmr::vector<Marker_Item> prepared_marker_items{memory_resource(Memory_Domain::Other)};
std::pmr::vector<Label_Item> prepared_label_items{memory_resource(Memory_Domain::Other)};
void update_frequency_point_size(int frequency_point_size, Range frequency_range);
void set_frequency_data(std::pmr::vector<double>& line_data, const std::pmr::vector<double>* max_data, const std::pmr::vector<double>* min_data, const Spectrum_Render_State* s);
void update_marker_items(const Spectrum_Render_State* s, const Render_Frame_Snapshot& snapshot);
[[nodiscard]] double get_y(double x, bool& ok, Line_Interpolation_Mode mode);
void draw_power_curve(Canvas& canvas, const Render_Frame_Snapshot& snapshot, Spectrum_Render_State* s, const std::pmr::vector<double>& powers, const Pen& pen, const Brush& brush);
void draw_spectrum(Canvas& canvas, const Render_Frame_Snapshot& snapshot, Spectrum_Render_State* s);
void draw_marker_items(Canvas& canvas, const Render_Frame_Snapshot& snapshot, Spectrum_Render_State* s);
void draw_label_items(Canvas& canvas, const Render_Frame_Snapshot& snapshot, Spectrum_Render_State* s);
void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override;
void prepare_data(const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override {
auto s = render_state(frame_view);
auto input = render_input_data(frame_view);
if (!s || !input)
return;
if (cache_frequency_point_size != s->frequency_point_size || cache_frequency_range != s->frequency_range) {
update_frequency_point_size(s->frequency_point_size, s->frequency_range);
}
input->read_all([this, s](std::pmr::vector<double>& data, const std::pmr::vector<double>* max_data, const std::pmr::vector<double>* min_data) {
set_frequency_data(data, max_data, min_data, s);
});
if (has_power_data) {
update_marker_items(s, snapshot);
}
frame_view.consume_input();
}
bool select_test(const PointF& pos) override {
const auto sc = edit_state();
const auto frequency_axis = sc->frequency_axis.lock();
const auto power_axis = sc->power_axis.lock();
if (!frequency_axis || !power_axis)
return false;
Axis_Mapping_2D mapping = Axis_Render_Access::mapping(frequency_axis.get(), power_axis.get());
const double frequency_val = mapping.domain.pixel_to_coord(mapping.domain_pixel(pos));
bool ok;
const double power = get_y(frequency_val, ok, sc->interpolation_mode);
if (!ok)
return false;
return std::abs(mapping.value.coord_to_pixel(power) - mapping.value_pixel(pos)) < 4.0;
}
void set_hover_state(Point pos, bool active) override {
Render_Edit_Lease<Spectrum_Private, Spectrum_Render_State> edit(this);
edit->hover_position = pos;
edit->hover_info_active = active;
}
};
} // namespace renderive