75 lines
3.6 KiB
C++
75 lines
3.6 KiB
C++
#include "Constellation_Diagram.h"
|
|
#include "../render/Blend2D_Cache.h"
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
#include <cmath>
|
|
#include <deque>
|
|
#include <numbers>
|
|
namespace renderive {
|
|
namespace detail {
|
|
namespace {
|
|
struct Timed_Point {
|
|
PointF point;
|
|
std::chrono::steady_clock::time_point time;
|
|
};
|
|
struct Constellation_Runtime_Base {};
|
|
struct Constellation_Runtime {
|
|
std::deque<Timed_Point> points;
|
|
};
|
|
using Constellation_Runtime_State = Double_State_Strategy<Constellation_Runtime_Base, Constellation_Runtime>;
|
|
}
|
|
struct Constellation_Diagram_Control::Impl {
|
|
Impl(std::shared_ptr<Axis> i, std::shared_ptr<Axis> q) : i_axis(std::move(i)), q_axis(std::move(q)) {}
|
|
std::shared_ptr<Axis> i_axis;
|
|
std::shared_ptr<Axis> q_axis;
|
|
Constellation_Runtime_State runtime;
|
|
};
|
|
Constellation_Diagram_Control::Constellation_Diagram_Control(Plot_Core& plot, const Constellation_Diagram_Properties& properties, std::shared_ptr<Axis> i_axis, std::shared_ptr<Axis> q_axis)
|
|
: Plottable_State(plot, properties), impl_(std::make_unique<Impl>(std::move(i_axis), std::move(q_axis))) {}
|
|
Constellation_Diagram_Control::~Constellation_Diagram_Control() = default;
|
|
void Constellation_Diagram_Control::append_point(PointF point) {
|
|
const auto now = std::chrono::steady_clock::now();
|
|
const int lifetime = get<&Constellation_Diagram_Properties::point_lifetime_ms>();
|
|
impl_->runtime.update([point, now, lifetime](Constellation_Runtime& runtime) {
|
|
runtime.points.push_back({point, now});
|
|
const auto cutoff = now - std::chrono::milliseconds(lifetime);
|
|
while(!runtime.points.empty() && runtime.points.front().time < cutoff)
|
|
runtime.points.pop_front();
|
|
});
|
|
changed();
|
|
}
|
|
std::size_t Constellation_Diagram_Control::point_count() const {
|
|
return impl_->runtime.read([](const Constellation_Runtime& runtime) { return runtime.points.size(); });
|
|
}
|
|
void Constellation_Diagram_Control::fit_square_to_axes() {
|
|
const auto state = properties();
|
|
const double side = std::max(state.i_range.size(), state.q_range.size());
|
|
impl_->i_axis->set_coord_range({state.i_range.center() - side * 0.5, state.i_range.center() + side * 0.5});
|
|
impl_->q_axis->set_coord_range({state.q_range.center() + side * 0.5, state.q_range.center() - side * 0.5});
|
|
}
|
|
void Constellation_Diagram_Control::publish() {
|
|
publish_properties();
|
|
impl_->runtime.publish();
|
|
}
|
|
void Constellation_Diagram_Control::paint(Painter& painter) {
|
|
const auto state = render_properties();
|
|
const auto runtime = impl_->runtime.render_use_state();
|
|
const Axis_Transform x = impl_->i_axis->transform();
|
|
const Axis_Transform y = impl_->q_axis->transform();
|
|
const int count = static_cast<int>(state.type);
|
|
const double radius = std::min(state.i_range.size(), state.q_range.size()) * 0.4;
|
|
for(int index = 0; index < count; ++index) {
|
|
const double angle = state.phase_offset_radians + 2.0 * std::numbers::pi * index / count;
|
|
const PointF point{state.i_range.center() + std::cos(angle) * radius, state.q_range.center() + std::sin(angle) * radius};
|
|
painter.circle({x.coord_to_pixel(point.x), y.coord_to_pixel(point.y)}, 3.0, Pen{state.anchor_color}, Brush{state.anchor_color, Brush_Style::Solid});
|
|
}
|
|
const auto cutoff = std::chrono::steady_clock::now() - std::chrono::milliseconds(state.point_lifetime_ms.get());
|
|
for(const auto& value : runtime.points) {
|
|
if(value.time < cutoff)
|
|
continue;
|
|
painter.circle({x.coord_to_pixel(value.point.x), y.coord_to_pixel(value.point.y)}, 2.0, Pen{state.point_color}, Brush{state.point_color, Brush_Style::Solid});
|
|
}
|
|
}
|
|
}
|
|
}
|