Files
Renderive/Core/plottable/Constellation_Diagram_p.h
T
2026-08-02 16:02:15 +08:00

157 lines
7.1 KiB
C++

#pragma once
#include <algorithm>
#include <cstddef>
#include <cstring>
#include <cmath>
#include <list>
#include <memory_resource>
#include "../architecture/Render_Lease.h"
#include "../Axis/Axis_p.h"
#include "../architecture/Bounded_Input_Buffer.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 "../render/Render_Frame_Snapshot.h"
#include "Constellation_Diagram.h"
namespace renderive {
struct Constellation_Diagram_Data {
PointF pos;
std::uint64_t birth_time_ns{};
std::uint64_t expire_time_ns{};
};
struct Constellation_Diagram_Input_Data : Input_Data {
static constexpr std::size_t Max_Pending_Point_Count = 128;
struct Packet {
double x{};
double y{};
};
Bounded_Input_Buffer data_list;
void push(PointF pos) {
Packet packet{pos.x, pos.y};
data_list.push(&packet, sizeof(packet), Max_Pending_Point_Count);
}
template <typename Handler>
void read_all(Handler handler) {
data_list.read_all([&handler](const void* data, std::size_t size) {
(void)size;
Packet packet;
std::memcpy(&packet, data, sizeof(packet));
handler(PointF{packet.x, packet.y});
});
}
void clear() override {
data_list.clear();
}
};
struct Constellation_Diagram_State : Render_State, Constellation_Diagram_Prop {};
struct Constellation_Diagram_Private : Typed_Render_Data<Constellation_Diagram, Constellation_Diagram_State, Constellation_Diagram_Input_Data> {
std::pmr::list<Constellation_Diagram_Data> data_list{memory_resource(Memory_Domain::Constellation_Diagram)};
std::pmr::vector<PointF> prepared_points{memory_resource(Memory_Domain::Constellation_Diagram)};
std::pmr::vector<Color> prepared_colors{memory_resource(Memory_Domain::Constellation_Diagram)};
std::pmr::vector<PointF> prepared_anchor_points{memory_resource(Memory_Domain::Constellation_Diagram)};
std::pmr::vector<PointF> anchor_points(const Constellation_Diagram_State* s) const {
std::pmr::vector<PointF> points(frame_memory_resource());
const unsigned order = static_cast<unsigned>(s->type);
if (order == 0)
return points;
constexpr double pi = 3.14159265358979323846;
const double center_x = s->i_range.center();
const double center_y = s->q_range.center();
const double radius = std::min(s->i_range.size(), s->q_range.size()) / 3.0;
points.reserve(order);
for (unsigned i = 0; i < order; ++i) {
const double angle = s->phase_offset_radians + 2.0 * pi * static_cast<double>(i) / static_cast<double>(order);
points.push_back({center_x + radius * std::cos(angle), center_y + radius * std::sin(angle)});
}
return points;
}
void refresh(std::uint64_t now_ns) {
for (auto it = data_list.begin(); it != data_list.end();) {
if (it->expire_time_ns <= now_ns)
data_list.erase(it++);
else
++it;
}
}
void prepare_data(const Render_Frame_Snapshot& snapshot) override {
sync_state_pipeline();
Constellation_Diagram_Input_Data* d = render_input_data();
Constellation_Diagram_State* s = render_state();
std::uint64_t now_ns = snapshot.frame_time_ns;
const std::uint64_t lifetime_ns = static_cast<std::uint64_t>(std::max(1, s->point_lifetime_ms)) * 1000000ull;
d->read_all([this, now_ns, lifetime_ns](const PointF& pos) {
Constellation_Diagram_Data data;
data.pos = pos;
data.birth_time_ns = now_ns;
data.expire_time_ns = now_ns + lifetime_ns;
data_list.push_back(data);
});
clear_render_input();
refresh(now_ns);
prepared_points.clear();
prepared_colors.clear();
prepared_points.reserve(static_cast<int>(data_list.size()));
prepared_colors.reserve(static_cast<int>(data_list.size()));
for (const Constellation_Diagram_Data& data : data_list) {
Color color = s->point_color;
const std::uint64_t total_ns = data.expire_time_ns > data.birth_time_ns ? data.expire_time_ns - data.birth_time_ns : 1;
const std::uint64_t remain_ns = data.expire_time_ns > now_ns ? data.expire_time_ns - now_ns : 0;
color.a = static_cast<std::uint8_t>(static_cast<std::uint64_t>(color.a) * remain_ns / total_ns);
prepared_points.push_back(data.pos);
prepared_colors.push_back(color);
}
prepared_anchor_points = anchor_points(s);
}
PointF map_point(const Axis_Mapping_2D& mapping, const PointF& point) const {
return mapping.map(point);
}
void draw_grid(Canvas& canvas, const Axis_Mapping_2D& mapping, const Constellation_Diagram_State* s) const {
canvas.set_pen(Pen{Color::white(), 1.0, Line_Style::Dot});
constexpr int divisions = 4;
for (int i = 0; i <= divisions; ++i) {
const double t = static_cast<double>(i) / static_cast<double>(divisions);
const double i_coord = s->i_range.origin + s->i_range.length() * t;
const double q_coord = s->q_range.origin + s->q_range.length() * t;
canvas.draw_line(mapping.map(i_coord, s->q_range.origin), mapping.map(i_coord, s->q_range.target));
canvas.draw_line(mapping.map(s->i_range.origin, q_coord), mapping.map(s->i_range.target, q_coord));
}
}
void draw_points(Canvas& canvas, const Axis_Mapping_2D& mapping) const {
canvas.set_antialias(true);
canvas.set_pen(Pen{.style = Line_Style::None});
for (std::size_t i = 0; i < prepared_points.size(); ++i) {
const Color color = i < prepared_colors.size() ? prepared_colors[i] : Color::red();
canvas.set_brush(Brush{color, Brush_Style::Solid});
canvas.draw_ellipse(map_point(mapping, prepared_points[i]), 2.0, 2.0);
}
}
void draw_anchors(Canvas& canvas, const Axis_Mapping_2D& mapping, Color color) const {
canvas.set_antialias(true);
canvas.set_pen(Pen{color, 2.0});
canvas.set_brush(Brush{});
constexpr double half = 6.0;
for (const PointF& point : prepared_anchor_points) {
PointF pixel = map_point(mapping, point);
canvas.draw_line(PointF{pixel.x - half, pixel.y}, PointF{pixel.x + half, pixel.y});
canvas.draw_line(PointF{pixel.x, pixel.y - half}, PointF{pixel.x, pixel.y + half});
}
}
void draw(Canvas& canvas, const Render_Frame_Snapshot&) override {
Constellation_Diagram_State* s = render_state();
auto i_axis = s->i_axis.lock();
auto q_axis = s->q_axis.lock();
if (!i_axis || !q_axis)
return;
Axis_Mapping_2D mapping = Axis_Render_Access::mapping(i_axis.get(), q_axis.get());
canvas.save();
draw_grid(canvas, mapping, s);
draw_points(canvas, mapping);
draw_anchors(canvas, mapping, s->anchor_color);
canvas.restore();
}
};
} // namespace renderive