#pragma once #include #include #include #include #include #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 cur_data{memory_resource(Memory_Domain::Spectrum)}; std::pmr::vector max_data{memory_resource(Memory_Domain::Spectrum)}; std::pmr::vector 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 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&& 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 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 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(marker_list.size()); } void select_prev_marker() { if (marker_list.empty()) return; cur_select_index = (cur_select_index - 1 + static_cast(marker_list.size())) % static_cast(marker_list.size()); } void set_select_index(int index) { if (index < 0 || index >= static_cast(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(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 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(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, 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 frequency{memory_resource(Memory_Domain::Spectrum)}; std::pmr::vector max_powers{memory_resource(Memory_Domain::Spectrum)}; std::pmr::vector cur_powers{memory_resource(Memory_Domain::Spectrum)}; std::pmr::vector min_powers{memory_resource(Memory_Domain::Spectrum)}; std::pmr::vector prepared_marker_items{memory_resource(Memory_Domain::Other)}; std::pmr::vector 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& line_data, const std::pmr::vector* max_data, const std::pmr::vector* 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& 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& data, const std::pmr::vector* max_data, const std::pmr::vector* 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 edit(this); edit->hover_position = pos; edit->hover_info_active = active; } }; } // namespace renderive