255 lines
7.3 KiB
C++
255 lines
7.3 KiB
C++
//
|
|
// Created by Codex on 2025/12/10.
|
|
//
|
|
|
|
#include "line_chart.hh"
|
|
#include <QLinearGradient>
|
|
#include <QMargins>
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <limits>
|
|
|
|
using creeper::line_widget::internal::LinePlot;
|
|
|
|
LinePlot::LinePlot() {
|
|
setBackground(Qt::transparent);
|
|
}
|
|
|
|
LinePlot::~LinePlot() = default;
|
|
|
|
void LinePlot::load_theme_manager(creeper::ThemeManager& mgr) {
|
|
mgr.append_handler(this, [this](const creeper::ThemeManager& manager) {
|
|
scheme_ = manager.color_scheme();
|
|
apply_theme();
|
|
if (initialized_) {
|
|
replot();
|
|
}
|
|
});
|
|
}
|
|
|
|
void LinePlot::set_data(const QVector<QPointF>& points) {
|
|
points_ = points;
|
|
trim_points();
|
|
if (initialized_) {
|
|
update_graph();
|
|
}
|
|
}
|
|
|
|
void LinePlot::set_max_points(int count) {
|
|
const int clamped = std::max(1, count);
|
|
if (clamped == max_points_) {
|
|
return;
|
|
}
|
|
max_points_ = clamped;
|
|
trim_points();
|
|
if (initialized_) {
|
|
update_graph();
|
|
}
|
|
}
|
|
|
|
void LinePlot::paintEvent(QPaintEvent* event) {
|
|
if (!initialized_) {
|
|
initialize_plot();
|
|
}
|
|
QCustomPlot::paintEvent(event);
|
|
}
|
|
|
|
void LinePlot::initialize_plot() {
|
|
if (initialized_) {
|
|
return;
|
|
}
|
|
|
|
axisRect()->setAutoMargins(QCP::msNone);
|
|
axisRect()->setMargins(QMargins(40, 12, 12, 18));
|
|
axisRect()->setBackground(QBrush(QColor(246, 249, 255)));
|
|
|
|
legend->setVisible(false);
|
|
xAxis->setVisible(true);
|
|
yAxis->setVisible(true);
|
|
xAxis->setRange(0.0, std::max(10, max_points_));
|
|
yAxis->setRange(0.0, default_y_range_);
|
|
|
|
xAxis->grid()->setVisible(false);
|
|
yAxis->grid()->setVisible(false);
|
|
xAxis->setTicks(false);
|
|
yAxis->setTicks(true);
|
|
xAxis->setSubTicks(false);
|
|
yAxis->setSubTicks(false);
|
|
xAxis->setTickLength(0);
|
|
yAxis->setTickLength(0);
|
|
yAxis->setTickLabelPadding(6);
|
|
yAxis->setLabelPadding(8);
|
|
|
|
graph_ = addGraph();
|
|
graph_->setLineStyle(QCPGraph::lsLine);
|
|
graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, QPen(Qt::NoPen), QBrush(Qt::white), 6));
|
|
graph_->setBrush(QColor(16, 54, 128, 42));
|
|
graph_->setAntialiased(true);
|
|
graph_->setAdaptiveSampling(true);
|
|
|
|
current_label_ = new QCPItemText(this);
|
|
current_label_->position->setType(QCPItemPosition::ptAxisRectRatio);
|
|
current_label_->setPositionAlignment(Qt::AlignRight | Qt::AlignTop);
|
|
current_label_->position->setCoords(0.98, 0.04);
|
|
current_label_->setPadding(QMargins(6, 3, 6, 3));
|
|
current_label_->setBrush(QColor(0, 0, 0, 50));
|
|
current_label_->setPen(Qt::NoPen);
|
|
current_label_->setLayer("overlay");
|
|
current_label_->setClipToAxisRect(true);
|
|
|
|
initialized_ = true;
|
|
apply_theme();
|
|
update_graph();
|
|
}
|
|
|
|
void LinePlot::reset_graph_range() {
|
|
xAxis->setRange(0.0, std::max(10, max_points_));
|
|
yAxis->setRange(0.0, default_y_range_);
|
|
}
|
|
|
|
void LinePlot::apply_theme() {
|
|
QColor line_color{ 16, 54, 128 };
|
|
QColor text_color{ 30, 30, 30 };
|
|
QColor bg_color{ 232, 238, 248 };
|
|
QColor label_bg = QColor(0, 0, 0, 50);
|
|
if (scheme_.has_value()) {
|
|
if (scheme_->primary.isValid()) {
|
|
line_color = scheme_->primary;
|
|
}
|
|
if (scheme_->on_surface.isValid()) {
|
|
text_color = scheme_->on_surface;
|
|
}
|
|
if (scheme_->surface_container_high.isValid()) {
|
|
bg_color = scheme_->surface_container_high;
|
|
}
|
|
else if (scheme_->surface_container.isValid()) {
|
|
bg_color = scheme_->surface_container;
|
|
}
|
|
else if (scheme_->surface.isValid()) {
|
|
bg_color = scheme_->surface;
|
|
}
|
|
if (scheme_->surface_container.isValid()) {
|
|
label_bg = QColor(scheme_->surface_container.red(),
|
|
scheme_->surface_container.green(),
|
|
scheme_->surface_container.blue(), 90);
|
|
}
|
|
}
|
|
|
|
QColor grid_color = bg_color;
|
|
|
|
QPen axis_pen(bg_color);
|
|
axis_pen.setWidthF(1.0);
|
|
xAxis->setBasePen(axis_pen);
|
|
yAxis->setBasePen(axis_pen);
|
|
xAxis->setTickPen(axis_pen);
|
|
yAxis->setTickPen(axis_pen);
|
|
xAxis->setTickLabelColor(Qt::transparent);
|
|
yAxis->setTickLabelColor(text_color);
|
|
xAxis->setLabelColor(Qt::transparent);
|
|
yAxis->setLabelColor(text_color);
|
|
axisRect()->setBackground(bg_color);
|
|
|
|
if (graph_) {
|
|
QPen pen(line_color);
|
|
pen.setWidthF(3.0);
|
|
pen.setCapStyle(Qt::RoundCap);
|
|
graph_->setPen(pen);
|
|
|
|
QLinearGradient fill_grad(0, 0, 0, 1);
|
|
fill_grad.setCoordinateMode(QGradient::CoordinateMode::ObjectBoundingMode);
|
|
fill_grad.setColorAt(0.0, QColor(line_color.red(), line_color.green(), line_color.blue(), 70));
|
|
fill_grad.setColorAt(1.0, QColor(line_color.red(), line_color.green(), line_color.blue(), 18));
|
|
graph_->setBrush(QBrush(fill_grad));
|
|
|
|
auto scatter = graph_->scatterStyle();
|
|
scatter.setPen(QPen(line_color, 1.5));
|
|
scatter.setBrush(QBrush(QColor(bg_color).lighter(104)));
|
|
scatter.setSize(7);
|
|
graph_->setScatterStyle(scatter);
|
|
}
|
|
|
|
if (current_label_) {
|
|
current_label_->setColor(text_color);
|
|
current_label_->setBrush(label_bg);
|
|
QFont f = current_label_->font();
|
|
f.setBold(true);
|
|
current_label_->setFont(f);
|
|
}
|
|
}
|
|
|
|
void LinePlot::update_graph() {
|
|
if (!initialized_) {
|
|
return;
|
|
}
|
|
if (!graph_) {
|
|
graph_ = addGraph();
|
|
apply_theme();
|
|
}
|
|
|
|
if (points_.isEmpty()) {
|
|
graph_->data()->clear();
|
|
reset_graph_range();
|
|
if (current_label_) {
|
|
current_label_->setText(QStringLiteral("--"));
|
|
}
|
|
replot();
|
|
return;
|
|
}
|
|
|
|
QVector<double> keys(points_.size());
|
|
QVector<double> values(points_.size());
|
|
double min_key = std::numeric_limits<double>::max();
|
|
double max_key = std::numeric_limits<double>::lowest();
|
|
double min_val = std::numeric_limits<double>::max();
|
|
double max_val = std::numeric_limits<double>::lowest();
|
|
|
|
for (int i = 0; i < points_.size(); ++i) {
|
|
const auto& pt = points_[i];
|
|
keys[i] = pt.x();
|
|
values[i] = pt.y();
|
|
min_key = std::min(min_key, pt.x());
|
|
max_key = std::max(max_key, pt.x());
|
|
min_val = std::min(min_val, pt.y());
|
|
max_val = std::max(max_val, pt.y());
|
|
}
|
|
|
|
graph_->setData(keys, values, true);
|
|
|
|
if (min_key == std::numeric_limits<double>::max()) {
|
|
reset_graph_range();
|
|
}
|
|
else {
|
|
const double key_span = std::max(1e-3, max_key - min_key);
|
|
xAxis->setRange(min_key, max_key + key_span * 0.02);
|
|
|
|
double value_span = max_val - min_val;
|
|
if (value_span < 1e-3) {
|
|
value_span = std::max(std::abs(max_val), 1.0);
|
|
min_val = max_val - value_span * 0.5;
|
|
}
|
|
const double padding = std::max(value_span * 0.25, 1.0);
|
|
yAxis->setRange(min_val - padding, max_val + padding);
|
|
}
|
|
|
|
if (current_label_) {
|
|
const double last_val = points_.back().y();
|
|
current_label_->setText(QString::number(last_val, 'f', 1));
|
|
}
|
|
|
|
replot();
|
|
}
|
|
|
|
void LinePlot::trim_points() {
|
|
if (max_points_ <= 0 || points_.size() <= max_points_) {
|
|
return;
|
|
}
|
|
const int start = points_.size() - max_points_;
|
|
points_ = points_.mid(start);
|
|
}
|
|
|
|
using namespace creeper;
|
|
|
|
void SumLinePlot::paintEvent(QPaintEvent* event) {
|
|
line_widget::internal::LinePlot::paintEvent(event);
|
|
}
|