双缓冲先加上
This commit is contained in:
+181
-285
@@ -1,17 +1,22 @@
|
||||
#include "Plottables.h"
|
||||
|
||||
#include "Spectrum.h"
|
||||
#include "Curve_Sampling.h"
|
||||
#include "../plot/Plot_Core.h"
|
||||
#include "../render/Blend2D_Cache.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
namespace renderive {
|
||||
namespace detail {
|
||||
namespace {
|
||||
|
||||
struct Spectrum_Runtime_Base {};
|
||||
struct Spectrum_Runtime {
|
||||
std::vector<double> samples;
|
||||
std::vector<double> maxima;
|
||||
std::vector<double> minima;
|
||||
std::vector<double> markers;
|
||||
int selected_marker = -1;
|
||||
Hover_Tooltip_Runtime tooltip;
|
||||
};
|
||||
using Spectrum_Runtime_State = Double_State_Strategy<Spectrum_Runtime_Base, Spectrum_Runtime>;
|
||||
RectF axes_rect(const Axis_Transform& horizontal, const Axis_Transform& vertical) {
|
||||
const double x1 = horizontal.coord_to_pixel(horizontal.coordinate_range.origin);
|
||||
const double x2 = horizontal.coord_to_pixel(horizontal.coordinate_range.target);
|
||||
@@ -19,21 +24,11 @@ RectF axes_rect(const Axis_Transform& horizontal, const Axis_Transform& vertical
|
||||
const double y2 = vertical.coord_to_pixel(vertical.coordinate_range.target);
|
||||
return {std::min(x1, x2), std::min(y1, y2), std::abs(x2 - x1), std::abs(y2 - y1)};
|
||||
}
|
||||
|
||||
void draw_curve(detail::Painter& painter,
|
||||
std::span<const double> values,
|
||||
Range domain,
|
||||
const Axis_Transform& x_axis,
|
||||
const Axis_Transform& y_axis,
|
||||
bool visible_only,
|
||||
Line_Interpolation_Mode interpolation,
|
||||
const Pen& pen,
|
||||
const Brush& brush) {
|
||||
auto points = detail::curve_points(values, domain, x_axis, y_axis,
|
||||
visible_only, interpolation);
|
||||
if (points.size() < 2)
|
||||
void draw_curve(Painter& painter, std::span<const double> values, Range domain, const Axis_Transform& x_axis, const Axis_Transform& y_axis, bool visible_only, Line_Interpolation_Mode interpolation, const Pen& pen, const Brush& brush) {
|
||||
auto points = curve_points(values, domain, x_axis, y_axis, visible_only, interpolation);
|
||||
if(points.size() < 2)
|
||||
return;
|
||||
if (brush.enabled()) {
|
||||
if(brush.enabled()) {
|
||||
std::vector<PointF> polygon;
|
||||
polygon.reserve(points.size() + 2);
|
||||
polygon.push_back({points.front().x, y_axis.coord_to_pixel(y_axis.coordinate_range.target)});
|
||||
@@ -43,314 +38,215 @@ void draw_curve(detail::Painter& painter,
|
||||
}
|
||||
painter.polyline(points, pen);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Spectrum::Spectrum(Plot_Core& plot,
|
||||
std::shared_ptr<Frequency_Axis> frequency_axis,
|
||||
std::shared_ptr<Axis> power_axis)
|
||||
: Renderable(plot), state_{} {
|
||||
state_.frequency_axis = std::move(frequency_axis);
|
||||
state_.power_axis = std::move(power_axis);
|
||||
}
|
||||
|
||||
#define RENDERIVE_SPECTRUM_PROPERTY(Type, Method, Field) \
|
||||
Type Spectrum::Method() const { std::lock_guard lock(mutex_); return state_.Field; } \
|
||||
void Spectrum::set_##Method(Type value) { { std::lock_guard lock(mutex_); state_.Field = std::move(value); } changed(); }
|
||||
|
||||
RENDERIVE_SPECTRUM_PROPERTY(std::shared_ptr<Frequency_Axis>, frequency_axis, frequency_axis)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(std::shared_ptr<Axis>, power_axis, power_axis)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(Range, frequency_range, frequency_range)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(double, center_frequency, center_frequency)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(Range, sweep_frequency_range, sweep_range)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(bool, max_hold_visible, max_hold)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(bool, min_hold_visible, min_hold)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(bool, max_marker_visible, max_marker)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(bool, use_min_marker, min_marker)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(bool, sweep_region_visible, sweep_visible)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(bool, visible_range_only, visible_only)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(Line_Interpolation_Mode, interpolation_mode, interpolation)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(Brush, max_brush, max_brush)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(Brush, current_brush, current_brush)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(Brush, min_brush, min_brush)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(Pen, max_pen, max_pen)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(Pen, current_pen, current_pen)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(Pen, min_pen, min_pen)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(Pen, selected_marker_pen, selected_marker_pen)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(Pen, marker_pen, marker_pen)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(Pen, middle_frequency_pen, middle_pen)
|
||||
RENDERIVE_SPECTRUM_PROPERTY(Brush, sweep_region_brush, sweep_brush)
|
||||
|
||||
#undef RENDERIVE_SPECTRUM_PROPERTY
|
||||
|
||||
int Spectrum::frequency_point_size() const {
|
||||
std::lock_guard lock(mutex_);
|
||||
return state_.point_count;
|
||||
}
|
||||
|
||||
void Spectrum::set_frequency_point_size(int value) {
|
||||
{ std::lock_guard lock(mutex_); state_.point_count = std::max(0, value); }
|
||||
changed();
|
||||
}
|
||||
|
||||
void Spectrum::update_samples(std::span<const double> values) {
|
||||
{
|
||||
std::lock_guard lock(mutex_);
|
||||
state_.samples.assign(values.begin(), values.end());
|
||||
if (state_.point_count <= 0)
|
||||
state_.point_count = static_cast<int>(values.size());
|
||||
if (state_.maxima.size() != values.size())
|
||||
state_.maxima.assign(values.begin(), values.end());
|
||||
else
|
||||
for (std::size_t index = 0; index < values.size(); ++index)
|
||||
state_.maxima[index] = std::max(state_.maxima[index], values[index]);
|
||||
if (state_.minima.size() != values.size())
|
||||
state_.minima.assign(values.begin(), values.end());
|
||||
else
|
||||
for (std::size_t index = 0; index < values.size(); ++index)
|
||||
state_.minima[index] = std::min(state_.minima[index], values[index]);
|
||||
}
|
||||
changed();
|
||||
}
|
||||
|
||||
void Spectrum::update_samples(std::pmr::vector<double>&& values) {
|
||||
update_samples(std::span<const double>(values.data(), values.size()));
|
||||
}
|
||||
|
||||
std::size_t Spectrum::sample_count() const {
|
||||
std::lock_guard lock(mutex_);
|
||||
return state_.samples.size();
|
||||
}
|
||||
|
||||
std::size_t Spectrum::rendered_point_count() const {
|
||||
const State state = state_snapshot();
|
||||
if (!state.frequency_axis || !state.power_axis)
|
||||
return 0;
|
||||
return detail::curve_points(state.samples, state.frequency_range,
|
||||
state.frequency_axis->transform(),
|
||||
state.power_axis->transform(), state.visible_only,
|
||||
state.interpolation)
|
||||
.size();
|
||||
}
|
||||
|
||||
double Spectrum::power_at(double frequency, bool& ok) const {
|
||||
std::lock_guard lock(mutex_);
|
||||
double spectrum_power_at(const Spectrum_Properties& properties, const Spectrum_Runtime& runtime, double frequency, bool& ok) {
|
||||
ok = false;
|
||||
if (state_.samples.empty() || !state_.frequency_range.contains(frequency) ||
|
||||
state_.frequency_range.length() == 0.0)
|
||||
if(runtime.samples.empty() || !properties.frequency_range.contains(frequency) || properties.frequency_range.length() == 0.0)
|
||||
return 0.0;
|
||||
const double normalized = (frequency - state_.frequency_range.origin) /
|
||||
state_.frequency_range.length();
|
||||
const double position = std::clamp(normalized, 0.0, 1.0) *
|
||||
static_cast<double>(state_.samples.size() - 1);
|
||||
const double normalized = (frequency - properties.frequency_range.origin) / properties.frequency_range.length();
|
||||
const double position = std::clamp(normalized, 0.0, 1.0) * static_cast<double>(runtime.samples.size() - 1);
|
||||
const auto lower = static_cast<std::size_t>(std::floor(position));
|
||||
const auto upper = std::min(lower + 1, state_.samples.size() - 1);
|
||||
const auto upper = std::min(lower + 1, runtime.samples.size() - 1);
|
||||
const double fraction = position - static_cast<double>(lower);
|
||||
ok = true;
|
||||
return state_.samples[lower] * (1.0 - fraction) + state_.samples[upper] * fraction;
|
||||
return runtime.samples[lower] * (1.0 - fraction) + runtime.samples[upper] * fraction;
|
||||
}
|
||||
|
||||
void Spectrum::add_custom_marker(double frequency) { add_custom_line_marker(frequency); }
|
||||
|
||||
void Spectrum::add_custom_line_marker(double frequency) {
|
||||
{ std::lock_guard lock(mutex_); state_.markers.push_back(frequency); }
|
||||
}
|
||||
struct Spectrum_Control::Impl {
|
||||
Impl(std::shared_ptr<Frequency_Axis> frequency, std::shared_ptr<Axis> power) : frequency_axis(std::move(frequency)), power_axis(std::move(power)) {}
|
||||
std::shared_ptr<Frequency_Axis> frequency_axis;
|
||||
std::shared_ptr<Axis> power_axis;
|
||||
Spectrum_Runtime_State runtime;
|
||||
};
|
||||
Spectrum_Control::Spectrum_Control(Plot_Core& plot, const Spectrum_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Axis> power_axis)
|
||||
: Plottable_State(plot, properties), impl_(std::make_unique<Impl>(std::move(frequency_axis), std::move(power_axis))) {}
|
||||
Spectrum_Control::~Spectrum_Control() = default;
|
||||
void Spectrum_Control::update_samples(std::span<const double> values) {
|
||||
if(get<&Spectrum_Properties::frequency_point_size>() <= 0)
|
||||
set<&Spectrum_Properties::frequency_point_size>(static_cast<int>(values.size()));
|
||||
impl_->runtime.update([values](Spectrum_Runtime& runtime) {
|
||||
runtime.samples.assign(values.begin(), values.end());
|
||||
if(runtime.maxima.size() != values.size())
|
||||
runtime.maxima.assign(values.begin(), values.end());
|
||||
else
|
||||
for(std::size_t index = 0; index < values.size(); ++index)
|
||||
runtime.maxima[index] = std::max(runtime.maxima[index], values[index]);
|
||||
if(runtime.minima.size() != values.size())
|
||||
runtime.minima.assign(values.begin(), values.end());
|
||||
else
|
||||
for(std::size_t index = 0; index < values.size(); ++index)
|
||||
runtime.minima[index] = std::min(runtime.minima[index], values[index]);
|
||||
});
|
||||
changed();
|
||||
}
|
||||
|
||||
void Spectrum::remove_custom_marker(double frequency) {
|
||||
{
|
||||
std::lock_guard lock(mutex_);
|
||||
if (state_.markers.empty())
|
||||
void Spectrum_Control::update_samples(std::pmr::vector<double>&& values) {
|
||||
update_samples(std::span<const double>(values.data(), values.size()));
|
||||
}
|
||||
std::size_t Spectrum_Control::sample_count() const {
|
||||
return impl_->runtime.read([](const Spectrum_Runtime& runtime) { return runtime.samples.size(); });
|
||||
}
|
||||
std::size_t Spectrum_Control::rendered_point_count() const {
|
||||
const auto state = properties();
|
||||
const auto runtime = impl_->runtime.read([](const Spectrum_Runtime& value) { return value; });
|
||||
return curve_points(runtime.samples, state.frequency_range, impl_->frequency_axis->transform(), impl_->power_axis->transform(), state.visible_range_only, state.interpolation_mode).size();
|
||||
}
|
||||
double Spectrum_Control::power_at(double frequency, bool& ok) const {
|
||||
const auto state = properties();
|
||||
return impl_->runtime.read([&](const Spectrum_Runtime& runtime) { return spectrum_power_at(state, runtime, frequency, ok); });
|
||||
}
|
||||
void Spectrum_Control::add_custom_marker(double frequency) {
|
||||
add_custom_line_marker(frequency);
|
||||
}
|
||||
void Spectrum_Control::add_custom_line_marker(double frequency) {
|
||||
impl_->runtime.update([frequency](Spectrum_Runtime& runtime) { runtime.markers.push_back(frequency); });
|
||||
changed();
|
||||
}
|
||||
void Spectrum_Control::remove_custom_marker(double frequency) {
|
||||
bool removed{};
|
||||
impl_->runtime.update([&](Spectrum_Runtime& runtime) {
|
||||
if(runtime.markers.empty())
|
||||
return;
|
||||
auto closest = std::min_element(state_.markers.begin(), state_.markers.end(),
|
||||
[frequency](double left, double right) {
|
||||
return std::abs(left - frequency) < std::abs(right - frequency);
|
||||
});
|
||||
const int removed = static_cast<int>(std::distance(state_.markers.begin(), closest));
|
||||
state_.markers.erase(closest);
|
||||
if (state_.selected_marker == removed)
|
||||
state_.selected_marker = -1;
|
||||
else if (state_.selected_marker > removed)
|
||||
--state_.selected_marker;
|
||||
}
|
||||
changed();
|
||||
auto closest = std::min_element(runtime.markers.begin(), runtime.markers.end(), [frequency](double left, double right) { return std::abs(left - frequency) < std::abs(right - frequency); });
|
||||
const int removed_index = static_cast<int>(std::distance(runtime.markers.begin(), closest));
|
||||
runtime.markers.erase(closest);
|
||||
if(runtime.selected_marker == removed_index)
|
||||
runtime.selected_marker = -1;
|
||||
else if(runtime.selected_marker > removed_index)
|
||||
--runtime.selected_marker;
|
||||
removed = true;
|
||||
});
|
||||
if(removed)
|
||||
changed();
|
||||
}
|
||||
|
||||
void Spectrum::remove_selected_marker() {
|
||||
{
|
||||
std::lock_guard lock(mutex_);
|
||||
if (state_.selected_marker < 0 || state_.selected_marker >= static_cast<int>(state_.markers.size()))
|
||||
void Spectrum_Control::remove_selected_marker() {
|
||||
bool removed{};
|
||||
impl_->runtime.update([&](Spectrum_Runtime& runtime) {
|
||||
if(runtime.selected_marker < 0 || runtime.selected_marker >= static_cast<int>(runtime.markers.size()))
|
||||
return;
|
||||
state_.markers.erase(state_.markers.begin() + state_.selected_marker);
|
||||
state_.selected_marker = -1;
|
||||
}
|
||||
runtime.markers.erase(runtime.markers.begin() + runtime.selected_marker);
|
||||
runtime.selected_marker = -1;
|
||||
removed = true;
|
||||
});
|
||||
if(removed)
|
||||
changed();
|
||||
}
|
||||
void Spectrum_Control::clear_custom_markers() {
|
||||
impl_->runtime.update([](Spectrum_Runtime& runtime) {
|
||||
runtime.markers.clear();
|
||||
runtime.selected_marker = -1;
|
||||
});
|
||||
changed();
|
||||
}
|
||||
|
||||
void Spectrum::clear_custom_markers() {
|
||||
{ std::lock_guard lock(mutex_); state_.markers.clear(); state_.selected_marker = -1; }
|
||||
int Spectrum_Control::selectable_line_marker_count() const {
|
||||
return impl_->runtime.read([](const Spectrum_Runtime& runtime) { return static_cast<int>(runtime.markers.size()); });
|
||||
}
|
||||
int Spectrum_Control::selected_marker_index() const {
|
||||
return impl_->runtime.get<&Spectrum_Runtime::selected_marker>();
|
||||
}
|
||||
void Spectrum_Control::set_selected_marker_index(int index) {
|
||||
impl_->runtime.update([index](Spectrum_Runtime& runtime) { runtime.selected_marker = index >= 0 && index < static_cast<int>(runtime.markers.size()) ? index : -1; });
|
||||
changed();
|
||||
}
|
||||
|
||||
int Spectrum::selectable_line_marker_count() const {
|
||||
std::lock_guard lock(mutex_);
|
||||
return static_cast<int>(state_.markers.size());
|
||||
}
|
||||
|
||||
int Spectrum::selected_marker_index() const { std::lock_guard lock(mutex_); return state_.selected_marker; }
|
||||
|
||||
void Spectrum::set_selected_marker_index(int index) {
|
||||
{
|
||||
std::lock_guard lock(mutex_);
|
||||
state_.selected_marker = index >= 0 && index < static_cast<int>(state_.markers.size()) ? index : -1;
|
||||
}
|
||||
void Spectrum_Control::select_next_marker() {
|
||||
impl_->runtime.update([](Spectrum_Runtime& runtime) {
|
||||
if(runtime.markers.empty())
|
||||
runtime.selected_marker = -1;
|
||||
else
|
||||
runtime.selected_marker = (runtime.selected_marker + 1) % static_cast<int>(runtime.markers.size());
|
||||
});
|
||||
changed();
|
||||
}
|
||||
|
||||
void Spectrum::select_next_marker() {
|
||||
std::lock_guard lock(mutex_);
|
||||
if (state_.markers.empty())
|
||||
state_.selected_marker = -1;
|
||||
else
|
||||
state_.selected_marker = (state_.selected_marker + 1) % static_cast<int>(state_.markers.size());
|
||||
invalidate_cache();
|
||||
plot().notify_model_dirty();
|
||||
void Spectrum_Control::select_previous_marker() {
|
||||
impl_->runtime.update([](Spectrum_Runtime& runtime) {
|
||||
if(runtime.markers.empty())
|
||||
runtime.selected_marker = -1;
|
||||
else
|
||||
runtime.selected_marker = (runtime.selected_marker <= 0 ? static_cast<int>(runtime.markers.size()) : runtime.selected_marker) - 1;
|
||||
});
|
||||
changed();
|
||||
}
|
||||
|
||||
void Spectrum::select_previous_marker() {
|
||||
std::lock_guard lock(mutex_);
|
||||
if (state_.markers.empty())
|
||||
state_.selected_marker = -1;
|
||||
else
|
||||
state_.selected_marker = (state_.selected_marker <= 0
|
||||
? static_cast<int>(state_.markers.size())
|
||||
: state_.selected_marker) - 1;
|
||||
invalidate_cache();
|
||||
plot().notify_model_dirty();
|
||||
void Spectrum_Control::clear_marker_selection() {
|
||||
set_selected_marker_index(-1);
|
||||
}
|
||||
|
||||
void Spectrum::clear_marker_selection() { set_selected_marker_index(-1); }
|
||||
|
||||
double Spectrum::marker_frequency(int index) const {
|
||||
std::lock_guard lock(mutex_);
|
||||
return index >= 0 && index < static_cast<int>(state_.markers.size()) ? state_.markers[index] : 0.0;
|
||||
double Spectrum_Control::marker_frequency(int index) const {
|
||||
return impl_->runtime.read([index](const Spectrum_Runtime& runtime) { return index >= 0 && index < static_cast<int>(runtime.markers.size()) ? runtime.markers[index] : 0.0; });
|
||||
}
|
||||
|
||||
void Spectrum::set_marker_frequency(int index, double frequency) {
|
||||
{
|
||||
std::lock_guard lock(mutex_);
|
||||
if (index < 0 || index >= static_cast<int>(state_.markers.size()))
|
||||
void Spectrum_Control::set_marker_frequency(int index, double frequency) {
|
||||
bool updated{};
|
||||
impl_->runtime.update([&](Spectrum_Runtime& runtime) {
|
||||
if(index < 0 || index >= static_cast<int>(runtime.markers.size()))
|
||||
return;
|
||||
state_.markers[index] = frequency;
|
||||
}
|
||||
changed();
|
||||
runtime.markers[index] = frequency;
|
||||
updated = true;
|
||||
});
|
||||
if(updated)
|
||||
changed();
|
||||
}
|
||||
|
||||
void Spectrum::set_current_marker_frequency(double frequency) {
|
||||
void Spectrum_Control::set_current_marker_frequency(double frequency) {
|
||||
const int index = selected_marker_index();
|
||||
if (index >= 0)
|
||||
if(index >= 0)
|
||||
set_marker_frequency(index, frequency);
|
||||
}
|
||||
|
||||
Spectrum::State Spectrum::state_snapshot() const {
|
||||
std::lock_guard lock(mutex_);
|
||||
return state_;
|
||||
void Spectrum_Control::handle_event(const Event& event) {
|
||||
bool updated{};
|
||||
impl_->runtime.update([&](Spectrum_Runtime& runtime) { updated = update_hover_tooltip(runtime.tooltip, event); });
|
||||
if(updated)
|
||||
changed();
|
||||
}
|
||||
|
||||
void Spectrum::handle_event(const Event& event) {
|
||||
update_hover(event);
|
||||
void Spectrum_Control::publish() {
|
||||
publish_properties();
|
||||
impl_->runtime.publish();
|
||||
}
|
||||
|
||||
void Spectrum::paint(detail::Painter& painter) {
|
||||
const State state = state_snapshot();
|
||||
if (!state.frequency_axis || !state.power_axis)
|
||||
return;
|
||||
const Axis_Transform horizontal = state.frequency_axis->transform();
|
||||
const Axis_Transform vertical = state.power_axis->transform();
|
||||
void Spectrum_Control::paint(Painter& painter) {
|
||||
const auto state = render_properties();
|
||||
const auto runtime = impl_->runtime.render_use_state();
|
||||
const Axis_Transform horizontal = impl_->frequency_axis->transform();
|
||||
const Axis_Transform vertical = impl_->power_axis->transform();
|
||||
const RectF content = axes_rect(horizontal, vertical);
|
||||
|
||||
if (state.sweep_visible) {
|
||||
const double first = horizontal.coord_to_pixel(state.sweep_range.origin);
|
||||
const double last = horizontal.coord_to_pixel(state.sweep_range.target);
|
||||
painter.rect({std::min(first, last), content.y, std::abs(last - first), content.height},
|
||||
Pen{.style = Line_Style::None}, state.sweep_brush);
|
||||
if(state.sweep_region_visible) {
|
||||
const double first = horizontal.coord_to_pixel(state.sweep_frequency_range.origin);
|
||||
const double last = horizontal.coord_to_pixel(state.sweep_frequency_range.target);
|
||||
painter.rect({std::min(first, last), content.y, std::abs(last - first), content.height}, Pen{.style = Line_Style::None}, state.sweep_region_brush);
|
||||
}
|
||||
if (state.max_hold)
|
||||
draw_curve(painter, state.maxima, state.frequency_range, horizontal, vertical,
|
||||
state.visible_only, state.interpolation, state.max_pen, state.max_brush);
|
||||
if (state.min_hold)
|
||||
draw_curve(painter, state.minima, state.frequency_range, horizontal, vertical,
|
||||
state.visible_only, state.interpolation, state.min_pen, state.min_brush);
|
||||
draw_curve(painter, state.samples, state.frequency_range, horizontal, vertical,
|
||||
state.visible_only, state.interpolation, state.current_pen, state.current_brush);
|
||||
|
||||
if (state.middle_pen.enabled()) {
|
||||
if(state.max_hold_visible)
|
||||
draw_curve(painter, runtime.maxima, state.frequency_range, horizontal, vertical, state.visible_range_only, state.interpolation_mode, state.max_pen, state.max_brush);
|
||||
if(state.min_hold_visible)
|
||||
draw_curve(painter, runtime.minima, state.frequency_range, horizontal, vertical, state.visible_range_only, state.interpolation_mode, state.min_pen, state.min_brush);
|
||||
draw_curve(painter, runtime.samples, state.frequency_range, horizontal, vertical, state.visible_range_only, state.interpolation_mode, state.current_pen, state.current_brush);
|
||||
if(state.middle_frequency_pen.enabled()) {
|
||||
const double x = horizontal.coord_to_pixel(state.center_frequency);
|
||||
painter.line({x, content.y}, {x, content.bottom()}, state.middle_pen);
|
||||
painter.line({x, content.y}, {x, content.bottom()}, state.middle_frequency_pen);
|
||||
}
|
||||
for (std::size_t index = 0; index < state.markers.size(); ++index) {
|
||||
const double frequency = state.markers[index];
|
||||
const double x = horizontal.coord_to_pixel(frequency);
|
||||
const Pen& pen = static_cast<int>(index) == state.selected_marker
|
||||
? state.selected_marker_pen
|
||||
: state.marker_pen;
|
||||
painter.line({x, content.y}, {x, content.bottom()}, pen);
|
||||
for(std::size_t index = 0; index < runtime.markers.size(); ++index) {
|
||||
const double x = horizontal.coord_to_pixel(runtime.markers[index]);
|
||||
painter.line({x, content.y}, {x, content.bottom()}, static_cast<int>(index) == runtime.selected_marker ? state.selected_marker_pen : state.marker_pen);
|
||||
}
|
||||
if (!state.samples.empty() && (state.max_marker || state.min_marker)) {
|
||||
if(!runtime.samples.empty() && (state.max_marker_visible || state.use_min_marker)) {
|
||||
const auto draw_extreme = [&](bool maximum) {
|
||||
auto iterator = maximum ? std::max_element(state.samples.begin(), state.samples.end())
|
||||
: std::min_element(state.samples.begin(), state.samples.end());
|
||||
const std::size_t index = static_cast<std::size_t>(std::distance(state.samples.begin(), iterator));
|
||||
const double denominator = state.samples.size() > 1 ? state.samples.size() - 1.0 : 1.0;
|
||||
const double frequency = state.frequency_range.origin +
|
||||
state.frequency_range.length() * index / denominator;
|
||||
auto iterator = maximum ? std::max_element(runtime.samples.begin(), runtime.samples.end()) : std::min_element(runtime.samples.begin(), runtime.samples.end());
|
||||
const std::size_t index = static_cast<std::size_t>(std::distance(runtime.samples.begin(), iterator));
|
||||
const double denominator = runtime.samples.size() > 1 ? runtime.samples.size() - 1.0 : 1.0;
|
||||
const double frequency = state.frequency_range.origin + state.frequency_range.length() * index / denominator;
|
||||
const PointF point{horizontal.coord_to_pixel(frequency), vertical.coord_to_pixel(*iterator)};
|
||||
const Pen& pen = maximum ? state.max_pen : state.min_pen;
|
||||
painter.circle(point, 3.0, pen, Brush{pen.color, Brush_Style::Solid});
|
||||
};
|
||||
if (state.max_marker)
|
||||
if(state.max_marker_visible)
|
||||
draw_extreme(true);
|
||||
if (state.min_marker)
|
||||
if(state.use_min_marker)
|
||||
draw_extreme(false);
|
||||
}
|
||||
|
||||
const Hover_Tooltip_Snapshot tooltip = tooltip_snapshot();
|
||||
if (tooltip.enabled && tooltip.active && content.contains(tooltip.position)) {
|
||||
const double frequency = horizontal.pixel_to_coord(tooltip.position.x);
|
||||
if(state.tooltip_enabled && runtime.tooltip.active && content.contains(runtime.tooltip.position)) {
|
||||
const double frequency = horizontal.pixel_to_coord(runtime.tooltip.position.x);
|
||||
bool ok{};
|
||||
const double power = power_at(frequency, ok);
|
||||
if (ok) {
|
||||
const double power = spectrum_power_at(state, runtime, frequency, ok);
|
||||
if(ok) {
|
||||
std::ostringstream text;
|
||||
text << std::fixed << std::setprecision(2) << frequency << " Hz " << power;
|
||||
const RectF box{tooltip.position.x + 8.0, tooltip.position.y + 8.0, 170.0, 24.0};
|
||||
painter.rect(box, Pen{tooltip.text_pen.color}, tooltip.background);
|
||||
painter.text({box.x + 4.0, box.y + 3.0}, text.str(), tooltip.font, tooltip.text_pen);
|
||||
const RectF box{runtime.tooltip.position.x + 8.0, runtime.tooltip.position.y + 8.0, 170.0, 24.0};
|
||||
painter.rect(box, Pen{state.tooltip_text_pen.color}, state.tooltip_background_brush);
|
||||
painter.text({box.x + 4.0, box.y + 3.0}, text.str(), state.tooltip_font, state.tooltip_text_pen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spectrum::Builder::Builder(std::shared_ptr<Renderable> parent,
|
||||
std::shared_ptr<Frequency_Axis> frequency_axis,
|
||||
std::shared_ptr<Axis> power_axis)
|
||||
: parent_(std::move(parent)), frequency_axis_(std::move(frequency_axis)),
|
||||
power_axis_(std::move(power_axis)) {}
|
||||
|
||||
std::shared_ptr<Spectrum> Spectrum::Builder::build() {
|
||||
if (!parent_ || !frequency_axis_ || !power_axis_)
|
||||
return {};
|
||||
auto result = parent_->plot().make_renderable<Spectrum>(parent_, frequency_axis_, power_axis_);
|
||||
result->set_frequency_range(frequency_range_);
|
||||
result->set_frequency_point_size(point_count_);
|
||||
result->set_center_frequency(center_);
|
||||
result->set_sweep_frequency_range(sweep_range_);
|
||||
result->set_max_hold_visible(max_hold_);
|
||||
result->set_min_hold_visible(min_hold_);
|
||||
result->set_max_marker_visible(max_marker_);
|
||||
result->set_use_min_marker(min_marker_);
|
||||
result->set_sweep_region_visible(sweep_visible_);
|
||||
result->set_visible_range_only(visible_only_);
|
||||
result->set_interpolation_mode(interpolation_);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace renderive
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user