224 lines
9.7 KiB
C++
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
|