609 lines
26 KiB
C++
609 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();
|
|
Spectrum_Render_State* sc = reinterpret_cast<Spectrum_Render_State*>(pd->edit_state());
|
|
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, 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());
|
|
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) {
|
|
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) + frequency_axis->unit_text();
|
|
std::string power_text = std::to_string(power) + power_axis->unit_text();
|
|
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, Spectrum_Render_State* s) {
|
|
if (s->sweep_region_visible) {
|
|
s->draw_axis_rect(canvas, s->sweep_frequency_range, s->center_frequency);
|
|
}
|
|
}
|
|
void Spectrum_Private::draw_marker_items(Canvas& canvas, 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());
|
|
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, 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());
|
|
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&) {
|
|
auto s = render_state();
|
|
draw_power_curve(canvas, s, cur_powers, s->cur_pen, s->current_brush);
|
|
draw_spectrum(canvas, s);
|
|
if (s->max_hold_visible)
|
|
draw_power_curve(canvas, s, max_powers, s->max_pen, s->max_brush);
|
|
if (s->min_hold_visible)
|
|
draw_power_curve(canvas, s, min_powers, s->min_pen, s->min_brush);
|
|
draw_marker_items(canvas, s);
|
|
draw_label_items(canvas, s);
|
|
if (q()->hover_ok(q())) {
|
|
auto frequency_axis = s->frequency_axis.lock();
|
|
auto power_axis = s->power_axis.lock();
|
|
if (frequency_axis && power_axis)
|
|
q()->draw_hover_tooltip(&canvas, frequency_axis.get(), power_axis.get());
|
|
}
|
|
}
|
|
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);
|
|
pd->set_input_completion(input.lease, 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);
|
|
pd->set_input_completion(input.lease, 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) {
|
|
auto render_state = d()->edit_state();
|
|
if (mark_index < 0 || mark_index >= (int)render_state->marker_list.size())
|
|
return -1;
|
|
return render_state->marker_list[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() {
|
|
auto render_state = d()->edit_state();
|
|
return (int)render_state->marker_list.size();
|
|
}
|
|
int Spectrum::selected_marker_index() {
|
|
auto render_state = d()->edit_state();
|
|
return 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
|