Files
Renderive/Qt/plot/Plot.cpp
T
2026-08-02 03:43:39 +08:00

260 lines
8.6 KiB
C++

#include "Plot_p.h"
#include "../bridge/Qt_Event_Adapter.h"
#include "../bridge/Qt_Image_Adapter.h"
#include "../bridge/Qt_Presentation_Sink.h"
#include "Core/architecture/Render_Time.h"
#include "Core/base/global.h"
#include "Core/plottable/export.h"
#include <QPainter>
#include <QPointer>
#include <QResizeEvent>
#include <QTimer>
namespace renderive {
namespace {
Color from_qcolor(const QColor& color) {
return {
static_cast<std::uint8_t>(color.red()),
static_cast<std::uint8_t>(color.green()),
static_cast<std::uint8_t>(color.blue()),
static_cast<std::uint8_t>(color.alpha())
};
}
QColor to_qcolor(Color color) {
return QColor(color.r, color.g, color.b, color.a);
}
std::string to_utf8_string(const QString& text) {
QByteArray bytes = text.toUtf8();
return std::string(bytes.constData(), static_cast<std::size_t>(bytes.size()));
}
} // namespace
void start_render_scheduler(Render_Runtime_Config config) {
Global::instance()->start_render_scheduler(config);
}
Abs_Plot_Private::Abs_Plot_Private(std::unique_ptr<Refresh_Strategy> refresh_strategy)
: strategy(refresh_strategy.get()),
core(std::make_unique<Plot_Core>(std::move(refresh_strategy))) {}
Abs_Plot_Private::~Abs_Plot_Private() = default;
void Abs_Plot_Private::attach_widget(Abs_Plot* plot) {
presentation_sink = std::make_shared<Qt_Presentation_Sink>(plot);
core->set_presentation_sink(presentation_sink);
QPointer<Abs_Plot> guarded_plot(plot);
auto pending = performance_update_pending;
set_performance_overlay_update_callback(*core, [guarded_plot, pending]() mutable {
if (!guarded_plot || pending->exchange(true, std::memory_order_acq_rel))
return;
QTimer::singleShot(0, guarded_plot.data(), [guarded_plot, pending]() mutable {
pending->store(false, std::memory_order_release);
if (guarded_plot)
guarded_plot->update();
});
});
}
Abs_Plot::Abs_Plot(Abs_Plot_Private* private_data)
: d(private_data) {
d->attach_widget(this);
setAttribute(Qt::WA_OpaquePaintEvent, true);
setMouseTracking(true);
setFocusPolicy(Qt::NoFocus);
}
Abs_Plot::~Abs_Plot() {
if (d) {
set_performance_overlay_update_callback(*d->core, {});
d->core->pause_render();
delete d;
d = nullptr;
}
}
void Abs_Plot::init() {
init_renderable_tree();
}
void Abs_Plot::init_renderable_tree() {
d->core->init();
}
Plot_Core* Abs_Plot::core() const {
return d ? d->core.get() : nullptr;
}
std::shared_ptr<Renderable> Abs_Plot::root_renderable() const {
return d->core->root_renderable();
}
std::shared_ptr<Renderable> Abs_Plot::create_renderable_node(const std::shared_ptr<Renderable>& parent, const QString& object_name) const {
return d->core->create_renderable_node(parent, to_utf8_string(object_name));
}
void Abs_Plot::remove_renderable(const std::shared_ptr<Renderable>& able) {
d->core->remove_renderable(able);
}
bool Abs_Plot::is_rendering() {
return d->core->is_rendering();
}
void Abs_Plot::start_render() {
d->core->start_render();
}
void Abs_Plot::pause_render() {
d->core->pause_render();
}
QColor Abs_Plot::background_color() {
return to_qcolor(d->core->background_color());
}
void Abs_Plot::set_background_color(const QColor& color) {
d->core->set_background_color(from_qcolor(color));
}
void Abs_Plot::mark_render_state_dirty() {
d->core->mark_render_state_dirty();
}
void Abs_Plot::paintEvent(QPaintEvent* event) {
d->paint_event(this, event);
}
bool Abs_Plot::event(QEvent* event) {
bool base_result = QWidget::event(event);
return d->event(this, event, base_result);
}
void Abs_Plot_Private::paint_event(Abs_Plot* plot, QPaintEvent* event) {
(void)event;
std::uint64_t paint_begin_time = steady_now_ns();
auto frame = core->begin_present();
QPainter painter(plot);
if (frame && !frame.image().empty()) {
QImage image = Qt_Image_Adapter::to_qimage(frame.image());
painter.drawImage(QPoint(0, 0), image);
}
else {
painter.fillRect(plot->rect(), plot->background_color());
}
auto performance_snapshot = performance_overlay_snapshot(*core);
if (performance_snapshot && !performance_snapshot->image.empty()) {
Size current_size = core->viewport_size();
auto ctx = core->render_context();
std::uint64_t current_viewport_version = ctx ? ctx->viewport_version.load(std::memory_order_acquire) : 0;
if (performance_snapshot->viewport_size == current_size && performance_snapshot->viewport_version == current_viewport_version) {
QImage overlay = Qt_Image_Adapter::to_qimage(performance_snapshot->image.view());
const RectF& target = performance_snapshot->destination_rect;
painter.drawImage(QRectF(target.x, target.y, target.width, target.height), overlay);
}
}
painter.end();
core->end_present(std::move(frame), paint_begin_time, steady_now_ns());
}
void Abs_Plot_Private::request_performance_overlay_update(Abs_Plot* plot) {
if (!plot || performance_update_pending->exchange(true, std::memory_order_acq_rel))
return;
QPointer<Abs_Plot> guarded_plot(plot);
auto pending = performance_update_pending;
QTimer::singleShot(0, plot, [guarded_plot, pending]() mutable {
pending->store(false, std::memory_order_release);
if (guarded_plot)
guarded_plot->update();
});
}
bool Abs_Plot_Private::event(Abs_Plot* plot, QEvent* event, bool base_result) {
switch (event->type()) {
case QEvent::Resize: {
auto* resize_event = static_cast<QResizeEvent*>(event);
core->set_viewport_size(Qt_Event_Adapter::to_size(resize_event->size()));
notify_performance_overlay_viewport_changed(*core);
core->dispatch_event(Qt_Event_Adapter::to_resize_event(resize_event));
core->mark_render_state_dirty();
break;
}
case QEvent::Show: {
Event show_event(Event_Type::Show);
core->dispatch_event(show_event);
core->start_render();
break;
}
case QEvent::Hide: {
Event hide_event(Event_Type::Hide);
core->dispatch_event(hide_event);
core->pause_render();
break;
}
case QEvent::Leave: {
Event leave_event(Event_Type::Leave);
core->dispatch_event(leave_event);
break;
}
case QEvent::Wheel: {
auto wheel_event = Qt_Event_Adapter::to_wheel_event(static_cast<QWheelEvent*>(event));
if (performance_overlay_handle_wheel(*core, wheel_event.position, wheel_event.angle_delta_y, wheel_event.pixel_delta_y)) {
event->accept();
request_performance_overlay_update(plot);
return true;
}
core->dispatch_event(wheel_event);
break;
}
case QEvent::MouseMove: {
auto pointer_event = Qt_Event_Adapter::to_pointer_event(static_cast<QMouseEvent*>(event), Event_Type::Pointer_Move);
if (performance_overlay_handle_pointer_move(*core, pointer_event.position)) {
event->accept();
request_performance_overlay_update(plot);
return true;
}
core->dispatch_event(pointer_event);
break;
}
case QEvent::MouseButtonPress: {
auto* mouse_event = static_cast<QMouseEvent*>(event);
auto pointer_event = Qt_Event_Adapter::to_pointer_event(mouse_event, Event_Type::Pointer_Press);
if (mouse_event->button() == Qt::LeftButton && performance_overlay_handle_pointer_press(*core, pointer_event.position)) {
event->accept();
request_performance_overlay_update(plot);
return true;
}
core->dispatch_event(pointer_event);
break;
}
case QEvent::MouseButtonRelease: {
auto* mouse_event = static_cast<QMouseEvent*>(event);
auto pointer_event = Qt_Event_Adapter::to_pointer_event(mouse_event, Event_Type::Pointer_Release);
if (mouse_event->button() == Qt::LeftButton && performance_overlay_handle_pointer_release(*core, pointer_event.position)) {
event->accept();
request_performance_overlay_update(plot);
return true;
}
core->dispatch_event(pointer_event);
break;
}
case QEvent::KeyPress:
core->dispatch_event(Qt_Event_Adapter::to_key_event(static_cast<QKeyEvent*>(event), Event_Type::Key_Press));
break;
case QEvent::KeyRelease:
core->dispatch_event(Qt_Event_Adapter::to_key_event(static_cast<QKeyEvent*>(event), Event_Type::Key_Release));
break;
default:
break;
}
return base_result;
}
} // namespace renderive