颜色修改红绿,修复帧错误卡顿bug

This commit is contained in:
2025-12-16 14:25:48 +08:00
parent c86c24488c
commit a1f7f337c2
15 changed files with 2201 additions and 917 deletions

View File

@@ -127,10 +127,9 @@ public:
cpmp->setColorScale(color_scale);
QCPColorGradient gradient;
gradient.setColorStopAt(0.0, QColor(240, 246, 255)); // 低值淡色
gradient.setColorStopAt(0.35, QColor(142, 197, 252));
gradient.setColorStopAt(0.7, QColor(56, 128, 199));
gradient.setColorStopAt(1.0, QColor(8, 36, 95)); // 高值深色
gradient.setColorStopAt(0.0, QColor(0, 176, 80));
gradient.setColorStopAt(0.5, QColor(255, 214, 10));
gradient.setColorStopAt(1.0, QColor(204, 0, 0));
cpmp->setGradient(gradient);
cpmp->setDataRange(QCPRange(color_min, color_max));

View File

@@ -0,0 +1,221 @@
//
// Created by Codex on 2025/12/10.
//
#include "line_chart.hh"
#include <QLinearGradient>
#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);
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 };
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;
}
}
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);
}
}
void LinePlot::update_graph() {
if (!initialized_) {
return;
}
if (!graph_) {
graph_ = addGraph();
apply_theme();
}
if (points_.isEmpty()) {
graph_->data()->clear();
reset_graph_range();
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);
}
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);
}

View File

@@ -0,0 +1,79 @@
//
// Created by Codex on 2025/12/10.
//
#pragma once
#include "creeper-qt/utility/theme/theme.hh"
#include "creeper-qt/utility/wrapper/common.hh"
#include "creeper-qt/utility/wrapper/property.hh"
#include "creeper-qt/utility/wrapper/widget.hh"
#include "qcustomplot/qcustomplot.h"
#include <QPaintEvent>
#include <QPointF>
#include <concepts>
#include <optional>
#include <qsize.h>
#include <qvector.h>
namespace creeper {
class SumLinePlot;
namespace line_widget::internal {
class LinePlot: public QCustomPlot {
public:
LinePlot();
~LinePlot() override;
void load_theme_manager(ThemeManager& mgr);
void set_data(const QVector<QPointF>& points);
void set_max_points(int count);
protected:
void paintEvent(QPaintEvent* event) override;
private:
void initialize_plot();
void update_graph();
void apply_theme();
void reset_graph_range();
void trim_points();
QVector<QPointF> points_;
bool initialized_ = false;
std::optional<ColorScheme> scheme_;
QCPGraph* graph_ = nullptr;
int max_points_ = 240;
double default_y_range_ = 100.0;
};
} // namespace line_widget::internal
namespace line_widget::pro {
using Token = common::Token<internal::LinePlot>;
struct MaxPoints: Token {
int count;
explicit MaxPoints(int c): count{ c } { }
void apply(auto& self) const { self.set_max_points(count); }
};
using PlotData = DerivedProp<Token, QVector<QPointF>, [](auto& self, const auto& vec) {
self.set_data(vec);
}>;
template<class PlotWidget>
concept trait = std::derived_from<PlotWidget, Token>;
CREEPER_DEFINE_CHECKER(trait);
using namespace widget::pro;
using namespace theme::pro;
} // namespace line_widget::pro
struct SumLinePlot
: public Declarative<line_widget::internal::LinePlot,
CheckerOr<line_widget::pro::checker, widget::pro::checker, theme::pro::checker>> {
using Declarative::Declarative;
void paintEvent(QPaintEvent* event) override;
};
} // namespace creeper

View File

@@ -0,0 +1,202 @@
//
// Created by Codex on 2025/12/05.
//
#include "vector_field.hh"
#include <algorithm>
#include <cmath>
#include <vector>
using creeper::vector_widget::internal::VectorPlot;
VectorPlot::VectorPlot() {
setBackground(Qt::transparent);
}
VectorPlot::~VectorPlot() = default;
void VectorPlot::load_theme_manager(creeper::ThemeManager& mgr) {
mgr.append_handler(this, [this](const creeper::ThemeManager& manager) {
scheme_ = manager.color_scheme();
apply_color_scheme();
if (initialized_) {
replot();
}
});
}
void VectorPlot::set_matrix_size(const QSize& size) {
if (size == matrix_size_) {
return;
}
matrix_size_ = size;
reset_plot();
}
void VectorPlot::set_data(const QVector<PointData>& data) {
data_points_ = data;
if (initialized_) {
update_vectors();
}
}
void VectorPlot::paintEvent(QPaintEvent* event) {
if (!initialized_) {
initialize_plot();
}
QCustomPlot::paintEvent(event);
}
void VectorPlot::initialize_plot() {
if (initialized_) {
return;
}
xAxis->setVisible(false);
yAxis->setVisible(false);
xAxis->grid()->setPen(Qt::NoPen);
yAxis->grid()->setPen(Qt::NoPen);
xAxis->setRange(0.0, std::max(1, matrix_size_.width()));
yAxis->setRange(0.0, std::max(1, matrix_size_.height()));
xAxis->setSubTicks(true);
yAxis->setSubTicks(true);
xAxis->setTickLength(0);
yAxis->setTickLength(0);
xAxis->setSubTickLength(4);
yAxis->setSubTickLength(4);
initialized_ = true;
apply_color_scheme();
ensure_arrows();
update_vectors();
}
void VectorPlot::reset_plot() {
clearItems();
primary_arrow_ = nullptr;
initialized_ = false;
initialize_plot();
}
void VectorPlot::ensure_arrows() {
// no-op: legacy multi-arrow support removed
}
void VectorPlot::ensure_primary_arrow() {
if (primary_arrow_) {
return;
}
primary_arrow_ = new QCPItemLine(this);
primary_arrow_->start->setType(QCPItemPosition::ptPlotCoords);
primary_arrow_->end->setType(QCPItemPosition::ptPlotCoords);
primary_arrow_->setClipToAxisRect(true);
primary_arrow_->setClipAxisRect(axisRect());
primary_arrow_->setHead(QCPLineEnding(QCPLineEnding::esSpikeArrow, 14, 8));
primary_arrow_->setTail(QCPLineEnding(QCPLineEnding::esNone));
apply_color_scheme();
}
void VectorPlot::apply_color_scheme() {
QColor pen_color = arrow_color_;
if (scheme_.has_value() && scheme_->primary.isValid()) {
pen_color = scheme_->primary;
}
QPen strong_pen(pen_color);
strong_pen.setWidthF(8.0); // 稍细一点但仍然饱满
strong_pen.setCapStyle(Qt::FlatCap);
strong_pen.setJoinStyle(Qt::MiterJoin);
if (primary_arrow_) {
primary_arrow_->setPen(strong_pen);
}
}
void VectorPlot::update_vectors() {
if (!initialized_) {
return;
}
ensure_primary_arrow();
const int width = std::max(1, matrix_size_.width());
const int height = std::max(1, matrix_size_.height());
const int expected = width * height;
if (expected <= 0 || !primary_arrow_) {
replot();
return;
}
std::vector<double> values(static_cast<std::size_t>(expected), 0.0);
for (const auto& item: data_points_) {
const int x = static_cast<int>(item.x);
const int y = static_cast<int>(item.y);
if (x >= 0 && x < width && y >= 0 && y < height) {
const int idx = y * width + x;
values[static_cast<std::size_t>(idx)] = item.z;
}
}
auto value_at = [&](int x, int y) -> double {
if (x < 0 || x >= width || y < 0 || y >= height) {
return 0.0;
}
return values[static_cast<std::size_t>(y * width + x)];
};
std::vector<std::pair<double, double>> grads(static_cast<std::size_t>(expected), { 0.0, 0.0 });
double max_mag = 0.0;
double sum_gx = 0.0;
double sum_gy = 0.0;
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
const double gx = 0.5 * (value_at(x + 1, y) - value_at(x - 1, y));
const double gy = 0.5 * (value_at(x, y + 1) - value_at(x, y - 1));
const double mag = std::sqrt(gx * gx + gy * gy);
grads[static_cast<std::size_t>(y * width + x)] = { gx, gy };
max_mag = std::max(max_mag, mag);
sum_gx += gx;
sum_gy += gy;
}
}
const double scale = (max_mag > 1e-6) ? (0.35 / max_mag) : 0.0;
const bool fallback = max_mag <= 1e-6;
const double mid_x = static_cast<double>(width) * 0.5;
const double mid_y = static_cast<double>(height) * 0.5;
double dir_x = sum_gx;
double dir_y = sum_gy;
if (fallback || std::abs(dir_x) + std::abs(dir_y) < 1e-6) {
dir_x = 0.0;
dir_y = -1.0; // 默认向上指,保证可见
}
double dir_len = std::sqrt(dir_x * dir_x + dir_y * dir_y);
if (dir_len < 1e-6) {
dir_x = 0.0;
dir_y = -1.0;
dir_len = 1.0;
}
dir_x /= dir_len;
dir_y /= dir_len;
const double arrow_len = 0.48 * std::min(width, height); // 稍长的指针
const double tail_ratio = 0.25; // 穿过中心的尾巴略长
const double tail_len = arrow_len * tail_ratio;
const double head_len = arrow_len - tail_len;
const double backoff = std::max(0.5, head_len * 0.12); // 轻微回缩,避免方头顶出
const double head_base = std::max(0.0, head_len - backoff);
const double cx = static_cast<double>(width) * 0.5;
const double cy = static_cast<double>(height) * 0.5;
if (primary_arrow_) {
primary_arrow_->start->setCoords(cx - dir_x * tail_len, cy - dir_y * tail_len);
primary_arrow_->end->setCoords(cx + dir_x * head_base, cy + dir_y * head_base);
primary_arrow_->setVisible(true);
}
replot();
}
using namespace creeper;
void VectorFieldPlot::paintEvent(QPaintEvent* event) {
vector_widget::internal::VectorPlot::paintEvent(event);
}

View File

@@ -0,0 +1,82 @@
//
// Created by Codex on 2025/12/05.
//
#pragma once
#include "components/charts/heatmap.hh" // for PointData definition
#include "creeper-qt/utility/theme/theme.hh"
#include "creeper-qt/utility/wrapper/common.hh"
#include "creeper-qt/utility/wrapper/property.hh"
#include "creeper-qt/utility/wrapper/widget.hh"
#include "qcustomplot/qcustomplot.h"
#include <optional>
#include <QPaintEvent>
#include <concepts>
#include <qsize.h>
#include <qvector.h>
namespace creeper {
class VectorFieldPlot;
namespace vector_widget::internal {
class VectorPlot : public QCustomPlot {
public:
VectorPlot();
~VectorPlot() override;
void load_theme_manager(ThemeManager& mgr);
void set_matrix_size(const QSize& size);
void set_data(const QVector<PointData>& data);
protected:
void paintEvent(QPaintEvent* event) override;
private:
void initialize_plot();
void reset_plot();
void update_vectors();
void ensure_arrows();
void apply_color_scheme();
QSize matrix_size_{ 3, 4 };
QVector<PointData> data_points_;
bool initialized_ = false;
std::optional<ColorScheme> scheme_;
QColor arrow_color_{ 16, 54, 128 }; // 深蓝色
QCPItemLine* primary_arrow_ = nullptr;
void ensure_primary_arrow();
};
} // namespace vector_widget::internal
namespace vector_widget::pro {
using Token = common::Token<internal::VectorPlot>;
struct MatrixSize : Token {
QSize size;
explicit MatrixSize(const QSize& s) : size{ s } { }
explicit MatrixSize(int w, int h) : size{ w, h } { }
void apply(auto& self) const { self.set_matrix_size(size); }
};
using PlotData = DerivedProp<Token, QVector<PointData>, [](auto& self, const auto& vec) {
self.set_data(vec);
}>;
template<class PlotWidget>
concept trait = std::derived_from<PlotWidget, Token>;
CREEPER_DEFINE_CHECKER(trait);
using namespace widget::pro;
using namespace theme::pro;
} // namespace vector_widget::pro
struct VectorFieldPlot
: public Declarative<vector_widget::internal::VectorPlot,
CheckerOr<vector_widget::pro::checker, widget::pro::checker, theme::pro::checker>> {
using Declarative::Declarative;
void paintEvent(QPaintEvent* event) override;
};
} // namespace creeper