#include "tools/cabana/chart/sparkline.h" #include #include #include 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::max(); max_val = std::numeric_limits::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()); } }