You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
100 lines
3.2 KiB
100 lines
3.2 KiB
#include "tools/cabana/chart/sparkline.h"
|
|
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <QPainter>
|
|
|
|
void Sparkline::update(const cabana::Signal *sig, CanEventIter first, CanEventIter last, int range, QSize size) {
|
|
if (first == last || size.isEmpty()) {
|
|
pixmap = QPixmap();
|
|
return;
|
|
}
|
|
|
|
points_.clear();
|
|
min_val = std::numeric_limits<double>::max();
|
|
max_val = std::numeric_limits<double>::lowest();
|
|
points_.reserve(std::distance(first, last));
|
|
|
|
uint64_t start_time = (*first)->mono_time;
|
|
double value = 0.0;
|
|
for (auto it = first; it != last; ++it) {
|
|
if (sig->getValue((*it)->dat, (*it)->size, &value)) {
|
|
min_val = std::min(min_val, value);
|
|
max_val = std::max(max_val, value);
|
|
points_.emplace_back(((*it)->mono_time - start_time) / 1e9, value);
|
|
}
|
|
}
|
|
|
|
if (points_.empty()) {
|
|
pixmap = QPixmap();
|
|
return;
|
|
}
|
|
|
|
freq_ = points_.size() / std::max(points_.back().x() - points_.front().x(), 1.0);
|
|
render(sig->color, range, size);
|
|
}
|
|
|
|
void Sparkline::render(const QColor &color, int range, QSize size) {
|
|
// Adjust for flat lines
|
|
bool is_flat_line = min_val == max_val;
|
|
if (is_flat_line) {
|
|
min_val -= 1.0;
|
|
max_val += 1.0;
|
|
}
|
|
|
|
// Calculate scaling
|
|
const double xscale = (size.width() - 1) / (double)range;
|
|
const double yscale = (size.height() - 3) / (max_val - min_val);
|
|
bool draw_individual_points = (points_.back().x() * xscale / points_.size()) > 8.0;
|
|
|
|
// Transform or downsample points
|
|
render_points_.reserve(points_.size());
|
|
render_points_.clear();
|
|
if (draw_individual_points) {
|
|
for (const auto &p : points_) {
|
|
render_points_.emplace_back(p.x() * xscale, 1.0 + (max_val - p.y()) * yscale);
|
|
}
|
|
} else if (is_flat_line) {
|
|
double y = size.height() / 2.0;
|
|
render_points_.emplace_back(0.0, y);
|
|
render_points_.emplace_back(points_.back().x() * xscale, y);
|
|
} else {
|
|
double prev_y = points_.front().y();
|
|
render_points_.emplace_back(points_.front().x() * xscale, 1.0 + (max_val - prev_y) * yscale);
|
|
bool in_flat = false;
|
|
|
|
for (size_t i = 1; i < points_.size(); ++i) {
|
|
const auto &p = points_[i];
|
|
double y = p.y();
|
|
if (std::abs(y - prev_y) < 1e-6) {
|
|
in_flat = true;
|
|
} else {
|
|
if (in_flat) render_points_.emplace_back(points_[i - 1].x() * xscale, 1.0 + (max_val - prev_y) * yscale);
|
|
render_points_.emplace_back(p.x() * xscale, 1.0 + (max_val - y) * yscale);
|
|
in_flat = false;
|
|
}
|
|
prev_y = y;
|
|
}
|
|
if (in_flat) render_points_.emplace_back(points_.back().x() * xscale, 1.0 + (max_val - prev_y) * yscale);
|
|
}
|
|
|
|
// Render to pixmap
|
|
qreal dpr = qApp->devicePixelRatio();
|
|
const QSize pixmap_size = size * dpr;
|
|
if (pixmap.size() != pixmap_size) {
|
|
pixmap = QPixmap(pixmap_size);
|
|
}
|
|
pixmap.setDevicePixelRatio(dpr);
|
|
pixmap.fill(Qt::transparent);
|
|
QPainter painter(&pixmap);
|
|
painter.setRenderHint(QPainter::Antialiasing, render_points_.size() <= 500);
|
|
painter.setPen(color);
|
|
painter.drawPolyline(render_points_.data(), render_points_.size());
|
|
|
|
painter.setPen(QPen(color, 3));
|
|
if (draw_individual_points) {
|
|
painter.drawPoints(render_points_.data(), render_points_.size());
|
|
} else {
|
|
painter.drawPoint(render_points_.back());
|
|
}
|
|
}
|
|
|