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

611 lines
26 KiB
C++

#include <algorithm>
#include <limits>
#include "../base/Frame_Memory.h"
#include "../base/Memory.h"
#include "../primitive/Curve.h"
#include "../primitive/Curve_Utils_p.h"
#include "../primitive/Label.h"
#include "../primitive/Marker.h"
#include "Spectrum_p.h"
namespace renderive {
RENDERIVE_DEFINE_RENDERABLE_BINDING(Spectrum, Spectrum_Private, Spectrum_Render_State, Spectrum_Input_Data, "Spectrum")
std::shared_ptr<Frequency_Axis> Spectrum::frequency_axis() {
return d()->edit_state_value(&Spectrum_Render_State::frequency_axis).lock();
}
void Spectrum::set_frequency_axis(std::shared_ptr<Frequency_Axis> value) {
d()->set_state_value(&Spectrum_Render_State::frequency_axis, std::weak_ptr<Frequency_Axis>(value));
}
std::shared_ptr<Axis> Spectrum::power_axis() {
return d()->edit_state_value(&Spectrum_Render_State::power_axis).lock();
}
void Spectrum::set_power_axis(std::shared_ptr<Axis> value) {
d()->set_state_value(&Spectrum_Render_State::power_axis, std::weak_ptr<Axis>(value));
}
int Spectrum::frequency_point_size() {
return d()->edit_state_value(&Spectrum_Render_State::frequency_point_size);
}
Range Spectrum::frequency_range() {
return d()->edit_state_value(&Spectrum_Render_State::frequency_range);
}
double Spectrum::center_frequency() {
return d()->edit_state_value(&Spectrum_Render_State::center_frequency);
}
void Spectrum::set_center_frequency(double value) {
d()->set_state_value(&Spectrum_Render_State::center_frequency, value);
}
Range Spectrum::sweep_frequency_range() {
return d()->edit_state_value(&Spectrum_Render_State::sweep_frequency_range);
}
void Spectrum::set_sweep_frequency_range(Range value) {
if (value.length() == 0.0)
return;
d()->set_state_value(&Spectrum_Render_State::sweep_frequency_range, std::move(value));
}
bool Spectrum::max_hold_visible() {
return d()->edit_state_value(&Spectrum_Render_State::max_hold_visible);
}
void Spectrum::set_max_hold_visible(bool value) {
d()->set_state_value(&Spectrum_Render_State::max_hold_visible, value);
}
bool Spectrum::min_hold_visible() {
return d()->edit_state_value(&Spectrum_Render_State::min_hold_visible);
}
void Spectrum::set_min_hold_visible(bool value) {
d()->set_state_value(&Spectrum_Render_State::min_hold_visible, value);
}
bool Spectrum::max_marker_visible() {
return d()->edit_state_value(&Spectrum_Render_State::max_marker_visible);
}
void Spectrum::set_max_marker_visible(bool value) {
d()->set_state_value(&Spectrum_Render_State::max_marker_visible, value);
}
bool Spectrum::use_min_marker() {
return d()->edit_state_value(&Spectrum_Render_State::use_min_marker);
}
void Spectrum::set_use_min_marker(bool value) {
d()->set_state_value(&Spectrum_Render_State::use_min_marker, value);
}
bool Spectrum::sweep_region_visible() {
return d()->edit_state_value(&Spectrum_Render_State::sweep_region_visible);
}
void Spectrum::set_sweep_region_visible(bool value) {
d()->set_state_value(&Spectrum_Render_State::sweep_region_visible, value);
}
bool Spectrum::visible_range_only() {
return d()->edit_state_value(&Spectrum_Render_State::visible_range_only);
}
void Spectrum::set_visible_range_only(bool value) {
d()->set_state_value(&Spectrum_Render_State::visible_range_only, value);
}
Line_Interpolation_Mode Spectrum::interpolation_mode() {
return d()->edit_state_value(&Spectrum_Render_State::interpolation_mode);
}
void Spectrum::set_interpolation_mode(Line_Interpolation_Mode value) {
d()->set_state_value(&Spectrum_Render_State::interpolation_mode, value);
}
Line_Sampling_Strategy Spectrum::sample_mode() {
return d()->edit_state_value(&Spectrum_Render_State::sampling_strategy);
}
void Spectrum::set_sample_mode(Line_Sampling_Strategy value) {
d()->set_state_value(&Spectrum_Render_State::sampling_strategy, value);
}
Brush Spectrum::max_brush() {
return d()->edit_state_value(&Spectrum_Render_State::max_brush);
}
void Spectrum::set_max_brush(Brush value) {
d()->set_state_value(&Spectrum_Render_State::max_brush, std::move(value));
}
Brush Spectrum::current_brush() {
return d()->edit_state_value(&Spectrum_Render_State::current_brush);
}
void Spectrum::set_current_brush(Brush value) {
d()->set_state_value(&Spectrum_Render_State::current_brush, std::move(value));
}
Brush Spectrum::min_brush() {
return d()->edit_state_value(&Spectrum_Render_State::min_brush);
}
void Spectrum::set_min_brush(Brush value) {
d()->set_state_value(&Spectrum_Render_State::min_brush, std::move(value));
}
Pen Spectrum::max_pen() {
return d()->edit_state_value(&Spectrum_Render_State::max_pen);
}
void Spectrum::set_max_pen(Pen value) {
d()->set_state_value(&Spectrum_Render_State::max_pen, std::move(value));
}
Pen Spectrum::current_pen() {
return d()->edit_state_value(&Spectrum_Render_State::cur_pen);
}
void Spectrum::set_current_pen(Pen value) {
d()->set_state_value(&Spectrum_Render_State::cur_pen, std::move(value));
}
Pen Spectrum::min_pen() {
return d()->edit_state_value(&Spectrum_Render_State::min_pen);
}
void Spectrum::set_min_pen(Pen value) {
d()->set_state_value(&Spectrum_Render_State::min_pen, std::move(value));
}
Pen Spectrum::selected_marker_pen() {
return d()->edit_state_value(&Spectrum_Render_State::selected_marker_pen);
}
void Spectrum::set_selected_marker_pen(Pen value) {
d()->set_state_value(&Spectrum_Render_State::selected_marker_pen, std::move(value));
}
Pen Spectrum::marker_pen() {
return d()->edit_state_value(&Spectrum_Render_State::marker_pen);
}
void Spectrum::set_marker_pen(Pen value) {
d()->set_state_value(&Spectrum_Render_State::marker_pen, std::move(value));
}
Pen Spectrum::middle_frequency_pen() {
return d()->edit_state_value(&Spectrum_Render_State::middle_frequency_pen);
}
void Spectrum::set_middle_frequency_pen(Pen value) {
d()->set_state_value(&Spectrum_Render_State::middle_frequency_pen, std::move(value));
}
Brush Spectrum::sweep_region_brush() {
return d()->edit_state_value(&Spectrum_Render_State::sweep_region_brush);
}
void Spectrum::set_sweep_region_brush(Brush value) {
d()->set_state_value(&Spectrum_Render_State::sweep_region_brush, std::move(value));
}
void Spectrum::set_frequency_point_size(int frequency_point_size) {
Render_Edit_Lease<Spectrum_Private, Spectrum_Render_State> edit(d());
edit->frequency_point_size = std::max(1, frequency_point_size);
}
void Spectrum::set_frequency_range(Range frequency_range) {
if (frequency_range.length() == 0.0)
return;
Render_Edit_Lease<Spectrum_Private, Spectrum_Render_State> edit(d());
edit->frequency_range = frequency_range;
}
Spectrum::Builder::Builder(std::shared_ptr<Renderable> parent, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Axis> power_axis) {
ASSERT(parent, "Spectrum build error! parent == nullptr");
ASSERT(frequency_axis && power_axis, "Spectrum build error! axis == nullptr");
ASSERT(frequency_axis->attached_to_plot(), "Spectrum build error! frequency_axis->plot == nullptr");
ASSERT(frequency_axis->shares_plot_with(*power_axis), "Spectrum build error! frequency_axis->plot != power_axis->plot");
ASSERT(parent->shares_plot_with(*frequency_axis), "Spectrum build error! parent->plot != axis->plot");
ASSERT(frequency_axis->orientation() != power_axis->orientation(), "Spectrum build error! axes must be orthogonal");
this->parent = parent;
this->frequency_axis = frequency_axis;
this->power_axis = power_axis;
}
std::shared_ptr<Spectrum> Spectrum::Builder::build() {
auto ret = renderive::make_shared<Spectrum>();
ret->init(parent);
Spectrum_Private* pd = ret->d();
Render_Edit_Lease<Spectrum_Private, Spectrum_Render_State> edit(pd);
Spectrum_Render_State* sc = edit.operator->();
PROP_R(std::weak_ptr<Frequency_Axis>, frequency_axis)
PROP_R(std::weak_ptr<Axis>, power_axis);
PROP_R(int, frequency_point_size);
PROP_R(Range, frequency_range);
PROP_R(double, center_frequency);
PROP_R(Range, sweep_frequency_range);
PROP_R(bool, max_hold_visible);
PROP_R(bool, min_hold_visible);
PROP_R(bool, max_marker_visible);
PROP_R(bool, use_min_marker);
PROP_R(bool, sweep_region_visible);
PROP_R(bool, visible_range_only);
PROP_R(Line_Interpolation_Mode, interpolation_mode);
PROP_R(Line_Sampling_Strategy, sampling_strategy);
PROP_R(Brush, max_brush);
PROP_R(Brush, current_brush);
PROP_R(Brush, min_brush);
PROP_R(Pen, max_pen);
PROP_R(Pen, cur_pen);
PROP_R(Pen, min_pen);
PROP_R(Pen, selected_marker_pen);
PROP_R(Pen, marker_pen);
PROP_R(Pen, middle_frequency_pen);
PROP_R(Brush, sweep_region_brush);
sc->frequency_point_size = std::max(1, sc->frequency_point_size);
if (sc->frequency_range.length() == 0.0)
sc->frequency_range = {0.0, 1.0};
if (sc->sweep_frequency_range.length() == 0.0)
sc->sweep_frequency_range = {0.0, 1.0};
return ret;
}
void Spectrum_Private::update_frequency_point_size(int frequency_point_size, Range frequency_range) {
if (frequency_point_size <= 0) {
cache_frequency_point_size = frequency_point_size;
cache_frequency_range = frequency_range;
has_power_data = false;
max_powers.clear();
cur_powers.clear();
min_powers.clear();
frequency.clear();
return;
}
cache_frequency_point_size = frequency_point_size;
cache_frequency_range = frequency_range;
has_power_data = false;
max_powers.resize(frequency_point_size);
cur_powers.resize(frequency_point_size);
min_powers.resize(frequency_point_size);
frequency.resize(frequency_point_size);
if (frequency_point_size == 1) {
frequency[0] = frequency_range.origin;
return;
}
double cur = frequency_range.origin;
double step = frequency_range.length() / (frequency_point_size - 1);
frequency[0] = frequency_range.origin;
for (int i = 1; i < frequency_point_size; ++i) {
cur += step;
frequency[i] = cur;
}
}
void Spectrum_Private::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) {
if (s->frequency_point_size <= 0)
return;
if (line_data.size() != s->frequency_point_size)
return;
if ((max_data && max_data->size() != s->frequency_point_size) || (min_data && min_data->size() != s->frequency_point_size))
return;
cur_powers.swap(line_data);
if (!has_power_data) {
max_powers.assign(cur_powers.begin(), cur_powers.end());
min_powers.assign(cur_powers.begin(), cur_powers.end());
has_power_data = true;
}
double min = std::numeric_limits<double>::max();
double max = std::numeric_limits<double>::lowest();
int min_index = 0, max_index = 0;
for (int i = 0; i < s->frequency_point_size; ++i) {
double& cur_data = cur_powers[i];
if (s->max_hold_visible) {
double hold_max = max_data ? max_data->at(i) : cur_data;
if (hold_max > max_powers.at(i))
max_powers[i] = hold_max;
}
if (s->min_hold_visible) {
double hold_min = min_data ? min_data->at(i) : cur_data;
if (hold_min < min_powers.at(i))
min_powers[i] = hold_min;
}
if (s->max_marker_visible && cur_data > max) {
max = cur_data;
max_index = i;
}
if (s->use_min_marker && cur_data < min) {
min = cur_data;
min_index = i;
}
}
max_marker_key = frequency[max_index];
max_marker_value = cur_powers[max_index];
min_marker_key = frequency[min_index];
min_marker_value = cur_powers[min_index];
}
double Spectrum_Private::get_y(double x, bool& ok, Line_Interpolation_Mode mode) {
if (cur_powers.size() < 2 || cache_frequency_range.length() == 0.0 || !cache_frequency_range.contains(x)) {
ok = false;
return -1;
}
double index = coordinate_to_source_index(cache_frequency_range, cur_powers.size(), x);
ok = true;
return interpolate_sample_value(index, cur_powers.size(), mode, [this](int i) {
return cur_powers.at(i);
});
}
void Spectrum_Private::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) {
auto frequency_axis = s->frequency_axis.lock();
auto power_axis = s->power_axis.lock();
if (!frequency_axis || !power_axis)
return;
if (powers.size() < 2 || s->frequency_range.length() == 0.0)
return;
Axis_Mapping_2D mapping = Axis_Render_Access::mapping(frequency_axis.get(), power_axis.get(), snapshot);
std::pmr::vector<PointF> points(frame_memory_resource());
int point_count = static_cast<int>(powers.size());
if (s->sampling_strategy == Line_Sampling_Strategy::Pixel_Min_Max_Envelope) {
points = Curve_Utils::build_pixel_envelope_points(mapping, s->frequency_range, point_count, point_count, s->visible_range_only, [&powers](int i) {
return powers.at(i);
});
}
else {
points = Curve_Utils::build_resampled_points(mapping, s->frequency_range, point_count, point_count, s->visible_range_only, s->interpolation_mode, [&powers](int i) {
return powers.at(i);
});
}
canvas.save();
Curve_Utils::draw_polyline(&canvas, points, pen);
Curve_Utils::draw_fill_to_value(&canvas, points, mapping, mapping.value.coord_range.target, brush);
canvas.restore();
}
void Spectrum_Private::update_marker_items(const Spectrum_Render_State* s, const Render_Frame_Snapshot& snapshot) {
auto frequency_axis = s->frequency_axis.lock();
auto power_axis = s->power_axis.lock();
prepared_marker_items.clear();
prepared_label_items.clear();
if (!frequency_axis || !power_axis)
return;
auto append_marker = [&](double frequency, double power, bool draw_line, bool selected) {
Pen pen = selected ? s->selected_marker_pen : s->marker_pen;
Marker_Item marker;
marker.position = PointF(frequency, power);
marker.radius = 2.0;
marker.pen = pen;
marker.mode = draw_line ? Marker_Mode::Domain_Line : Marker_Mode::Point;
prepared_marker_items.push_back(marker);
std::string frequency_text = std::to_string(frequency) + Axis_Render_Access::unit_text(frequency_axis.get(), snapshot);
std::string power_text = std::to_string(power) + Axis_Render_Access::unit_text(power_axis.get(), snapshot);
Label_Item frequency_label;
frequency_label.position = marker.position;
frequency_label.offset = PointF(0.0, -28.0);
frequency_label.text = frequency_text;
frequency_label.alignment = Text_Align::Horizontal_Center | Text_Align::Bottom;
frequency_label.text_pen = pen;
prepared_label_items.push_back(frequency_label);
Label_Item power_label = frequency_label;
power_label.offset = PointF(0.0, -14.0);
power_label.text = power_text;
prepared_label_items.push_back(power_label);
};
if (s->max_marker_visible)
append_marker(max_marker_key, max_marker_value, false, false);
if (s->use_min_marker)
append_marker(min_marker_key, min_marker_value, false, false);
for (int i = 0; i < (int)s->marker_list.size(); ++i) {
const Spectrum_Marker& custom_marker = s->marker_list.at(i);
bool ok;
double value = get_y(custom_marker.frequency, ok, s->interpolation_mode);
if (ok)
append_marker(custom_marker.frequency, value, custom_marker.is_line_marker, i == s->cur_select_index);
}
}
void Spectrum_Private::draw_spectrum(Canvas& canvas, const Render_Frame_Snapshot& snapshot, Spectrum_Render_State* s) {
if (s->sweep_region_visible) {
s->draw_axis_rect(canvas, snapshot, s->sweep_frequency_range, s->center_frequency);
}
}
void Spectrum_Private::draw_marker_items(Canvas& canvas, const Render_Frame_Snapshot& snapshot, Spectrum_Render_State* s) {
auto frequency_axis = s->frequency_axis.lock();
auto power_axis = s->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();
for (const Marker_Item& item : prepared_marker_items) {
PointF point = mapping.map(item.position);
PointF domain_start = mapping.map(item.position.x, mapping.value.coord_range.origin);
PointF domain_end = mapping.map(item.position.x, mapping.value.coord_range.target);
PointF value_start = mapping.map(mapping.domain.coord_range.origin, item.position.y);
PointF value_end = mapping.map(mapping.domain.coord_range.target, item.position.y);
canvas.set_pen(item.pen);
canvas.set_brush(item.brush);
if (item.mode == Marker_Mode::Point) {
canvas.draw_ellipse(point, item.radius, item.radius);
}
else if (item.mode == Marker_Mode::Domain_Line) {
canvas.draw_line(domain_start, domain_end);
}
else if (item.mode == Marker_Mode::Value_Line) {
canvas.draw_line(value_start, value_end);
}
else {
canvas.draw_line(domain_start, domain_end);
canvas.draw_line(value_start, value_end);
}
}
canvas.restore();
}
void Spectrum_Private::draw_label_items(Canvas& canvas, const Render_Frame_Snapshot& snapshot, Spectrum_Render_State* s) {
auto frequency_axis = s->frequency_axis.lock();
auto power_axis = s->power_axis.lock();
if (!frequency_axis || !power_axis)
return;
Axis_Mapping_2D mapping = Axis_Render_Access::mapping(frequency_axis.get(), power_axis.get(), snapshot);
for (const Label_Item& item : prepared_label_items) {
if (item.text.empty())
continue;
canvas.save();
canvas.set_font(item.font);
Text_Metrics tm = canvas.measure_text(item.text);
PointF anchor = mapping.map(item.position);
PointF top_left{anchor.x + item.offset.x, anchor.y + item.offset.y};
if ((item.alignment & Text_Align::Horizontal_Center) == Text_Align::Horizontal_Center)
top_left.x -= tm.width / 2.0;
else if ((item.alignment & Text_Align::Right) == Text_Align::Right)
top_left.x -= tm.width;
if ((item.alignment & Text_Align::Vertical_Center) == Text_Align::Vertical_Center)
top_left.y -= tm.height / 2.0;
else if ((item.alignment & Text_Align::Bottom) == Text_Align::Bottom)
top_left.y -= tm.height;
RectF rect{top_left.x, top_left.y, tm.width, tm.height};
if (item.background_brush.style != Brush_Style::None || item.border_pen.style != Line_Style::None) {
canvas.set_pen(item.border_pen);
canvas.set_brush(item.background_brush);
canvas.draw_rect(rect);
}
canvas.set_pen(item.text_pen);
canvas.draw_text(rect, Text_Align::Left | Text_Align::Vertical_Center, item.text);
canvas.restore();
}
}
void Spectrum_Private::draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) {
auto s = render_state(frame_view);
if (!s)
return;
draw_power_curve(canvas, snapshot, s, cur_powers, s->cur_pen, s->current_brush);
draw_spectrum(canvas, snapshot, s);
if (s->max_hold_visible)
draw_power_curve(canvas, snapshot, s, max_powers, s->max_pen, s->max_brush);
if (s->min_hold_visible)
draw_power_curve(canvas, snapshot, s, min_powers, s->min_pen, s->min_brush);
draw_marker_items(canvas, snapshot, s);
draw_label_items(canvas, snapshot, s);
if (s->can_show_hover(q())) {
auto frequency_axis = s->frequency_axis.lock();
auto power_axis = s->power_axis.lock();
if (frequency_axis && power_axis)
s->draw_hover_tooltip(&canvas, frequency_axis.get(), power_axis.get(), q(), &snapshot);
}
}
void Spectrum::update_samples(std::span<const double> line_data) {
if (!ok())
return;
Spectrum_Private* pd = d();
int frequency_point_size{};
bool use_max_line{};
bool use_min_line{};
{
Render_State_Lease<Spectrum_Private, Spectrum_Render_State> state(pd);
frequency_point_size = state->frequency_point_size;
use_max_line = state->max_hold_visible;
use_min_line = state->min_hold_visible;
}
if (line_data.size() != frequency_point_size)
return;
Render_Input_Lease<Spectrum_Private, Spectrum_Input_Data> input(pd);
input->push(line_data, use_max_line, use_min_line);
}
void Spectrum::update_samples(std::pmr::vector<double>&& line_data) {
if (!ok())
return;
Spectrum_Private* pd = d();
int frequency_point_size{};
bool use_max_line{};
bool use_min_line{};
{
Render_State_Lease<Spectrum_Private, Spectrum_Render_State> state(pd);
frequency_point_size = state->frequency_point_size;
use_max_line = state->max_hold_visible;
use_min_line = state->min_hold_visible;
}
if (line_data.size() != frequency_point_size)
return;
Render_Input_Lease<Spectrum_Private, Spectrum_Input_Data> input(pd);
input->push(std::move(line_data), use_max_line, use_min_line);
}
Update_Ticket Spectrum::submit_data(std::span<const double> line_data) {
auto update_state = make_update_state();
if (!ok()) {
update_state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled);
return Update_Ticket(update_state);
}
Spectrum_Private* pd = d();
int frequency_point_size{};
bool use_max_line{};
bool use_min_line{};
{
Render_State_Lease<Spectrum_Private, Spectrum_Render_State> state(pd);
frequency_point_size = state->frequency_point_size;
use_max_line = state->max_hold_visible;
use_min_line = state->min_hold_visible;
}
if (line_data.size() != frequency_point_size) {
update_state->complete_all(std::make_error_code(std::errc::invalid_argument), Update_Outcome::Cancelled);
return Update_Ticket(update_state);
}
{
Render_Input_Lease<Spectrum_Private, Spectrum_Input_Data> input(pd);
input->push(line_data, use_max_line, use_min_line);
input.set_completion(update_state);
}
update_state->complete_until(Update_Stage::Input_Released);
return Update_Ticket(update_state);
}
Update_Ticket Spectrum::submit_data(std::pmr::vector<double>&& line_data) {
auto update_state = make_update_state();
if (!ok()) {
update_state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled);
return Update_Ticket(update_state);
}
Spectrum_Private* pd = d();
int frequency_point_size{};
bool use_max_line{};
bool use_min_line{};
{
Render_State_Lease<Spectrum_Private, Spectrum_Render_State> state(pd);
frequency_point_size = state->frequency_point_size;
use_max_line = state->max_hold_visible;
use_min_line = state->min_hold_visible;
}
if (line_data.size() != frequency_point_size) {
update_state->complete_all(std::make_error_code(std::errc::invalid_argument), Update_Outcome::Cancelled);
return Update_Ticket(update_state);
}
{
Render_Input_Lease<Spectrum_Private, Spectrum_Input_Data> input(pd);
input->push(std::move(line_data), use_max_line, use_min_line);
input.set_completion(update_state);
}
update_state->complete_until(Update_Stage::Input_Released);
return Update_Ticket(update_state);
}
void Spectrum::add_custom_marker(double frequency) {
Render_Edit_Lease<Spectrum_Private, Spectrum_Render_State> edit(d());
edit->marker_list.push_back(Spectrum_Marker{frequency, false});
}
void Spectrum::add_custom_line_marker(double frequency) {
Render_Edit_Lease<Spectrum_Private, Spectrum_Render_State> edit(d());
edit->marker_list.push_back(Spectrum_Marker{frequency, true});
}
void Spectrum::remove_custom_marker(double frequency) {
Render_Edit_Lease<Spectrum_Private, Spectrum_Render_State> edit(d());
for (auto marker = edit->marker_list.begin(); marker != edit->marker_list.end(); ++marker) {
if (std::abs(marker->frequency - frequency) < 1e-12) {
edit->marker_list.erase(marker);
break;
}
}
}
void Spectrum::remove_selected_marker() {
Render_Edit_Lease<Spectrum_Private, Spectrum_Render_State> edit(d());
if (edit->cur_select_index < 0 || edit->cur_select_index >= (int)edit->marker_list.size())
return;
edit->marker_list.erase(edit->marker_list.begin() + edit->cur_select_index);
edit->cur_select_index = -1;
}
void Spectrum::clear_custom_markers() {
Render_Edit_Lease<Spectrum_Private, Spectrum_Render_State> edit(d());
edit->marker_list.clear();
}
double Spectrum::marker_frequency(int mark_index) {
Render_State_Lease<Spectrum_Private, Spectrum_Render_State> state(d());
if (!state || mark_index < 0 || mark_index >= static_cast<int>(state->marker_list.size()))
return -1;
return state->marker_list[static_cast<std::size_t>(mark_index)].frequency;
}
void Spectrum::set_marker_frequency(int marker_index, double frequency) {
Render_Edit_Lease<Spectrum_Private, Spectrum_Render_State> edit(d());
auto& markers = edit->marker_list;
if (marker_index < 0 || marker_index >= (int)markers.size())
return;
markers[marker_index].frequency = frequency;
}
void Spectrum::set_current_marker_frequency(double frequency) {
Render_Edit_Lease<Spectrum_Private, Spectrum_Render_State> edit(d());
edit->set_cur_frequency(frequency);
}
int Spectrum::selectable_line_marker_count() {
Render_State_Lease<Spectrum_Private, Spectrum_Render_State> state(d());
return state ? static_cast<int>(state->marker_list.size()) : 0;
}
int Spectrum::selected_marker_index() {
return d()->edit_state_value(&Spectrum_Render_State::cur_select_index);
}
void Spectrum::set_selected_marker_index(int marker_index) {
Render_Edit_Lease<Spectrum_Private, Spectrum_Render_State> edit(d());
edit->set_select_index(marker_index);
}
void Spectrum::select_next_marker() {
Render_Edit_Lease<Spectrum_Private, Spectrum_Render_State> edit(d());
edit->select_next_marker();
}
void Spectrum::select_previous_marker() {
Render_Edit_Lease<Spectrum_Private, Spectrum_Render_State> edit(d());
edit->select_prev_marker();
}
void Spectrum::clear_marker_selection() {
Render_Edit_Lease<Spectrum_Private, Spectrum_Render_State> edit(d());
edit->un_select();
}
double Spectrum::power_at(double frequency, bool& ok) {
return d()->get_y(frequency, ok, interpolation_mode());
}
} // namespace renderive