cabana/chart: populate the points incrementally (#30326)

* populate the points incrementally

* less spacing
old-commit-hash: 57621afcb0
testing-closet
Dean Lee 2 years ago committed by GitHub
parent 8123daceaf
commit 83678f7a87
  1. 86
      tools/cabana/chart/chart.cc
  2. 12
      tools/cabana/chart/chart.h
  3. 29
      tools/cabana/chart/chartswidget.cc
  4. 2
      tools/cabana/chart/chartswidget.h
  5. 26
      tools/cabana/streams/abstractstream.cc
  6. 8
      tools/cabana/streams/abstractstream.h
  7. 4
      tools/cabana/util.cc
  8. 4
      tools/cabana/util.h

@ -279,38 +279,50 @@ void ChartView::updateSeriesPoints() {
} }
} }
void ChartView::updateSeries(const cabana::Signal *sig, bool clear) { void ChartView::appendCanEvents(const cabana::Signal *sig, const std::vector<const CanEvent *> &events,
std::vector<QPointF> &vals, std::vector<QPointF> &step_vals) {
vals.reserve(vals.size() + events.capacity());
step_vals.reserve(step_vals.size() + events.capacity() * 2);
double value = 0;
const uint64_t begin_mono_time = can->routeStartTime() * 1e9;
for (const CanEvent *e : events) {
if (sig->getValue(e->dat, e->size, &value)) {
const double ts = (e->mono_time - std::min(e->mono_time, begin_mono_time)) / 1e9;
vals.emplace_back(ts, value);
if (!step_vals.empty())
step_vals.emplace_back(ts, step_vals.back().y());
step_vals.emplace_back(ts, value);
}
}
}
void ChartView::updateSeries(const cabana::Signal *sig, const MessageEventsMap *msg_new_events) {
for (auto &s : sigs) { for (auto &s : sigs) {
if (!sig || s.sig == sig) { if (!sig || s.sig == sig) {
if (clear) { if (!msg_new_events) {
s.vals.clear(); s.vals.clear();
s.step_vals.clear(); s.step_vals.clear();
s.last_value_mono_time = 0;
} }
auto events = msg_new_events ? msg_new_events : &can->eventsMap();
auto it = events->find(s.msg_id);
if (it == events->end() || it->second.empty()) continue;
const auto &msgs = can->events(s.msg_id); if (s.vals.empty() || (it->second.back()->mono_time / 1e9 - can->routeStartTime()) > s.vals.back().x()) {
s.vals.reserve(msgs.capacity()); appendCanEvents(s.sig, it->second, s.vals, s.step_vals);
s.step_vals.reserve(msgs.capacity() * 2); } else {
std::vector<QPointF> vals, step_vals;
auto first = std::upper_bound(msgs.cbegin(), msgs.cend(), s.last_value_mono_time, CompareCanEvent()); appendCanEvents(s.sig, it->second, vals, step_vals);
const double route_start_time = can->routeStartTime(); s.vals.insert(std::lower_bound(s.vals.begin(), s.vals.end(), vals.front().x(), xLessThan),
for (auto end = msgs.cend(); first != end; ++first) { vals.begin(), vals.end());
const CanEvent *e = *first; s.step_vals.insert(std::lower_bound(s.step_vals.begin(), s.step_vals.end(), step_vals.front().x(), xLessThan),
double value = 0; step_vals.begin(), step_vals.end());
if (s.sig->getValue(e->dat, e->size, &value)) {
double ts = e->mono_time / 1e9 - route_start_time; // seconds
s.vals.append({ts, value});
if (!s.step_vals.empty()) {
s.step_vals.append({ts, s.step_vals.back().y()});
}
s.step_vals.append({ts, value});
s.last_value_mono_time = e->mono_time;
}
} }
if (!can->liveStreaming()) { if (!can->liveStreaming()) {
s.segment_tree.build(s.vals); s.segment_tree.build(s.vals);
} }
s.series->replace(series_type == SeriesType::StepLine ? s.step_vals : s.vals); s.series->replace(QVector<QPointF>::fromStdVector(series_type == SeriesType::StepLine ? s.step_vals : s.vals));
} }
} }
updateAxisY(); updateAxisY();
@ -320,7 +332,7 @@ void ChartView::updateSeries(const cabana::Signal *sig, bool clear) {
// auto zoom on yaxis // auto zoom on yaxis
void ChartView::updateAxisY() { void ChartView::updateAxisY() {
if (sigs.isEmpty()) return; if (sigs.empty()) return;
double min = std::numeric_limits<double>::max(); double min = std::numeric_limits<double>::max();
double max = std::numeric_limits<double>::lowest(); double max = std::numeric_limits<double>::lowest();
@ -344,9 +356,7 @@ void ChartView::updateAxisY() {
if (it->y() > s.max) s.max = it->y(); if (it->y() > s.max) s.max = it->y();
} }
} else { } else {
auto [min_y, max_y] = s.segment_tree.minmax(std::distance(s.vals.cbegin(), first), std::distance(s.vals.cbegin(), last)); std::tie(s.min, s.max) = s.segment_tree.minmax(std::distance(s.vals.cbegin(), first), std::distance(s.vals.cbegin(), last));
s.min = min_y;
s.max = max_y;
} }
min = std::min(min, s.min); min = std::min(min, s.min);
max = std::max(max, s.max); max = std::max(max, s.max);
@ -365,7 +375,7 @@ void ChartView::updateAxisY() {
axis_y->setRange(min_y, max_y); axis_y->setRange(min_y, max_y);
axis_y->setTickCount(tick_count); axis_y->setTickCount(tick_count);
int n = std::max(int(-std::floor(std::log10((max_y - min_y) / (tick_count - 1)))), 0) + 1; int n = std::max(int(-std::floor(std::log10((max_y - min_y) / (tick_count - 1)))), 0);
int max_label_width = 0; int max_label_width = 0;
QFontMetrics fm(axis_y->labelsFont()); QFontMetrics fm(axis_y->labelsFont());
for (int i = 0; i < tick_count; i++) { for (int i = 0; i < tick_count; i++) {
@ -492,13 +502,9 @@ void ChartView::mouseReleaseEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton && rubber && rubber->isVisible()) { if (event->button() == Qt::LeftButton && rubber && rubber->isVisible()) {
rubber->hide(); rubber->hide();
auto rect = rubber->geometry().normalized(); auto rect = rubber->geometry().normalized();
double min = chart()->mapToValue(rect.topLeft()).x();
double max = chart()->mapToValue(rect.bottomRight()).x();
// Prevent zooming/seeking past the end of the route // Prevent zooming/seeking past the end of the route
min = std::clamp(min, 0., can->totalSeconds()); double min = std::clamp(chart()->mapToValue(rect.topLeft()).x(), 0., can->totalSeconds());
max = std::clamp(max, 0., can->totalSeconds()); double max = std::clamp(chart()->mapToValue(rect.bottomRight()).x(), 0., can->totalSeconds());
if (rubber->width() <= 0) { if (rubber->width() <= 0) {
// no rubber dragged, seek to mouse position // no rubber dragged, seek to mouse position
can->seekTo(min); can->seekTo(min);
@ -623,7 +629,7 @@ void ChartView::dropEvent(QDropEvent *event) {
source_chart->chart()->removeSeries(s.series); source_chart->chart()->removeSeries(s.series);
addSeries(s.series); addSeries(s.series);
} }
sigs.append(source_chart->sigs); sigs.insert(sigs.end(), std::move_iterator(source_chart->sigs.begin()), std::move_iterator(source_chart->sigs.end()));
updateAxisY(); updateAxisY();
updateTitle(); updateTitle();
startAnimation(); startAnimation();
@ -763,13 +769,8 @@ void ChartView::drawSignalValue(QPainter *painter) {
painter->setPen(chart()->legend()->labelColor()); painter->setPen(chart()->legend()->labelColor());
int i = 0; int i = 0;
for (auto &s : sigs) { for (auto &s : sigs) {
QString value = "--";
if (s.series->isVisible()) {
auto it = std::lower_bound(s.vals.crbegin(), s.vals.crend(), cur_sec, [](auto &p, double x) { return p.x() > x; }); auto it = std::lower_bound(s.vals.crbegin(), s.vals.crend(), cur_sec, [](auto &p, double x) { return p.x() > x; });
if (it != s.vals.crend() && it->x() >= axis_x->min()) { QString value = (it != s.vals.crend() && it->x() >= axis_x->min()) ? s.sig->formatValue(it->y()) : "--";
value = s.sig->formatValue(it->y());
}
}
QRectF marker_rect = legend_markers[i++]->sceneBoundingRect(); QRectF marker_rect = legend_markers[i++]->sceneBoundingRect();
QRectF value_rect(marker_rect.bottomLeft() - QPoint(0, 1), marker_rect.size()); QRectF value_rect(marker_rect.bottomLeft() - QPoint(0, 1), marker_rect.size());
QString elided_val = painter->fontMetrics().elidedText(value, Qt::ElideRight, value_rect.width()); QString elided_val = painter->fontMetrics().elidedText(value, Qt::ElideRight, value_rect.width());
@ -841,9 +842,8 @@ void ChartView::setSeriesType(SeriesType type) {
s.series->deleteLater(); s.series->deleteLater();
} }
for (auto &s : sigs) { for (auto &s : sigs) {
auto series = createSeries(series_type, s.sig->color); s.series = createSeries(series_type, s.sig->color);
series->replace(series_type == SeriesType::StepLine ? s.step_vals : s.vals); s.series->replace(QVector<QPointF>::fromStdVector(series_type == SeriesType::StepLine ? s.step_vals : s.vals));
s.series = series;
} }
updateSeriesPoints(); updateSeriesPoints();
updateTitle(); updateTitle();

@ -2,6 +2,7 @@
#include <tuple> #include <tuple>
#include <utility> #include <utility>
#include <vector>
#include <QMenu> #include <QMenu>
#include <QGraphicsPixmapItem> #include <QGraphicsPixmapItem>
@ -31,7 +32,7 @@ public:
ChartView(const std::pair<double, double> &x_range, ChartsWidget *parent = nullptr); ChartView(const std::pair<double, double> &x_range, ChartsWidget *parent = nullptr);
void addSignal(const MessageId &msg_id, const cabana::Signal *sig); void addSignal(const MessageId &msg_id, const cabana::Signal *sig);
bool hasSignal(const MessageId &msg_id, const cabana::Signal *sig) const; bool hasSignal(const MessageId &msg_id, const cabana::Signal *sig) const;
void updateSeries(const cabana::Signal *sig = nullptr, bool clear = true); void updateSeries(const cabana::Signal *sig = nullptr, const MessageEventsMap *msg_new_events = nullptr);
void updatePlot(double cur, double min, double max); void updatePlot(double cur, double min, double max);
void setSeriesType(SeriesType type); void setSeriesType(SeriesType type);
void updatePlotArea(int left, bool force = false); void updatePlotArea(int left, bool force = false);
@ -43,9 +44,8 @@ public:
MessageId msg_id; MessageId msg_id;
const cabana::Signal *sig = nullptr; const cabana::Signal *sig = nullptr;
QXYSeries *series = nullptr; QXYSeries *series = nullptr;
QVector<QPointF> vals; std::vector<QPointF> vals;
QVector<QPointF> step_vals; std::vector<QPointF> step_vals;
uint64_t last_value_mono_time = 0;
QPointF track_pt{}; QPointF track_pt{};
SegmentTree segment_tree; SegmentTree segment_tree;
double min = 0; double min = 0;
@ -64,6 +64,8 @@ private slots:
void signalRemoved(const cabana::Signal *sig) { removeIf([=](auto &s) { return s.sig == sig; }); } void signalRemoved(const cabana::Signal *sig) { removeIf([=](auto &s) { return s.sig == sig; }); }
private: private:
void appendCanEvents(const cabana::Signal *sig, const std::vector<const CanEvent *> &events,
std::vector<QPointF> &vals, std::vector<QPointF> &step_vals);
void createToolButtons(); void createToolButtons();
void addSeries(QXYSeries *series); void addSeries(QXYSeries *series);
void contextMenuEvent(QContextMenuEvent *event) override; void contextMenuEvent(QContextMenuEvent *event) override;
@ -107,7 +109,7 @@ private:
QGraphicsProxyWidget *close_btn_proxy; QGraphicsProxyWidget *close_btn_proxy;
QGraphicsProxyWidget *manage_btn_proxy; QGraphicsProxyWidget *manage_btn_proxy;
TipLabel tip_label; TipLabel tip_label;
QList<SigItem> sigs; std::vector<SigItem> sigs;
double cur_sec = 0; double cur_sec = 0;
SeriesType series_type = SeriesType::Line; SeriesType series_type = SeriesType::Line;
bool is_scrubbing = false; bool is_scrubbing = false;

@ -12,7 +12,7 @@
#include "tools/cabana/chart/chart.h" #include "tools/cabana/chart/chart.h"
const int MAX_COLUMN_COUNT = 4; const int MAX_COLUMN_COUNT = 4;
const int CHART_SPACING = 10; const int CHART_SPACING = 4;
ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), auto_scroll_timer(this), QFrame(parent) { ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), auto_scroll_timer(this), QFrame(parent) {
setFrameStyle(QFrame::StyledPanel | QFrame::Plain); setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
@ -78,8 +78,9 @@ ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), auto_scroll_tim
// charts // charts
charts_container = new ChartsContainer(this); charts_container = new ChartsContainer(this);
charts_container->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
charts_scroll = new QScrollArea(this); charts_scroll = new QScrollArea(this);
charts_scroll->viewport()->setBackgroundRole(QPalette::Base);
charts_scroll->setFrameStyle(QFrame::NoFrame); charts_scroll->setFrameStyle(QFrame::NoFrame);
charts_scroll->setWidgetResizable(true); charts_scroll->setWidgetResizable(true);
charts_scroll->setWidget(charts_container); charts_scroll->setWidget(charts_container);
@ -149,11 +150,10 @@ void ChartsWidget::updateTabBar() {
} }
} }
void ChartsWidget::eventsMerged() { void ChartsWidget::eventsMerged(const MessageEventsMap &new_events) {
QFutureSynchronizer<void> future_synchronizer; QFutureSynchronizer<void> future_synchronizer;
bool clear = !can->liveStreaming();
for (auto c : charts) { for (auto c : charts) {
future_synchronizer.addFuture(QtConcurrent::run(c, &ChartView::updateSeries, nullptr, clear)); future_synchronizer.addFuture(QtConcurrent::run(c, &ChartView::updateSeries, nullptr, &new_events));
} }
} }
@ -282,7 +282,7 @@ void ChartsWidget::splitChart(ChartView *src_chart) {
it->series->setColor(it->sig->color); it->series->setColor(it->sig->color);
c->addSeries(it->series); c->addSeries(it->series);
c->sigs.push_back(*it); c->sigs.emplace_back(std::move(*it));
c->updateAxisY(); c->updateAxisY();
c->updateTitle(); c->updateTitle();
it = src_chart->sigs.erase(it); it = src_chart->sigs.erase(it);
@ -322,7 +322,7 @@ void ChartsWidget::updateLayout(bool force) {
} }
for (int i = 0; i < current_charts.size(); ++i) { for (int i = 0; i < current_charts.size(); ++i) {
charts_layout->addWidget(current_charts[i], i / n, i % n); charts_layout->addWidget(current_charts[i], i / n, i % n);
if (current_charts[i]->sigs.isEmpty()) { if (current_charts[i]->sigs.empty()) {
// the chart will be resized after add signal. delay setVisible to reduce flicker. // the chart will be resized after add signal. delay setVisible to reduce flicker.
QTimer::singleShot(0, [c = current_charts[i]]() { c->setVisible(true); }); QTimer::singleShot(0, [c = current_charts[i]]() { c->setVisible(true); });
} else { } else {
@ -474,8 +474,9 @@ bool ChartsWidget::event(QEvent *event) {
ChartsContainer::ChartsContainer(ChartsWidget *parent) : charts_widget(parent), QWidget(parent) { ChartsContainer::ChartsContainer(ChartsWidget *parent) : charts_widget(parent), QWidget(parent) {
setAcceptDrops(true); setAcceptDrops(true);
setBackgroundRole(QPalette::Window);
QVBoxLayout *charts_main_layout = new QVBoxLayout(this); QVBoxLayout *charts_main_layout = new QVBoxLayout(this);
charts_main_layout->setContentsMargins(0, 10, 0, 0); charts_main_layout->setContentsMargins(0, CHART_SPACING, 0, CHART_SPACING);
charts_layout = new QGridLayout(); charts_layout = new QGridLayout();
charts_layout->setSpacing(CHART_SPACING); charts_layout->setSpacing(CHART_SPACING);
charts_main_layout->addLayout(charts_layout); charts_main_layout->addLayout(charts_layout);
@ -519,15 +520,11 @@ void ChartsContainer::paintEvent(QPaintEvent *ev) {
r.setHeight(CHART_SPACING); r.setHeight(CHART_SPACING);
} }
const int margin = (CHART_SPACING - 2) / 2;
QPainterPath path;
path.addPolygon(QPolygonF({r.topLeft(), QPointF(r.left() + CHART_SPACING, r.top() + r.height() / 2), r.bottomLeft()}));
path.addPolygon(QPolygonF({r.topRight(), QPointF(r.right() - CHART_SPACING, r.top() + r.height() / 2), r.bottomRight()}));
QPainter p(this); QPainter p(this);
p.setRenderHint(QPainter::Antialiasing); p.setPen(QPen(palette().highlight(), 2));
p.fillPath(path, palette().highlight()); p.drawLine(r.topLeft() + QPoint(1, 0), r.bottomLeft() + QPoint(1, 0));
p.fillRect(r.adjusted(2, margin, -2, -margin), palette().highlight()); p.drawLine(r.topLeft() + QPoint(0, r.height() / 2), r.topRight() + QPoint(0, r.height() / 2));
p.drawLine(r.topRight(), r.bottomRight());
} }
} }

@ -63,7 +63,7 @@ private:
void removeChart(ChartView *chart); void removeChart(ChartView *chart);
void splitChart(ChartView *chart); void splitChart(ChartView *chart);
QRect chartVisibleRect(ChartView *chart); QRect chartVisibleRect(ChartView *chart);
void eventsMerged(); void eventsMerged(const MessageEventsMap &new_events);
void updateState(); void updateState();
void zoomReset(); void zoomReset();
void startAutoScroll(); void startAutoScroll();

@ -1,7 +1,6 @@
#include "tools/cabana/streams/abstractstream.h" #include "tools/cabana/streams/abstractstream.h"
#include <algorithm> #include <algorithm>
#include <vector>
#include <QTimer> #include <QTimer>
@ -139,9 +138,10 @@ void AbstractStream::updateLastMsgsTo(double sec) {
} }
void AbstractStream::mergeEvents(std::vector<Event *>::const_iterator first, std::vector<Event *>::const_iterator last) { void AbstractStream::mergeEvents(std::vector<Event *>::const_iterator first, std::vector<Event *>::const_iterator last) {
static std::unordered_map<MessageId, std::deque<const CanEvent *>> new_events_map; static MessageEventsMap msg_events;
static std::vector<const CanEvent *> new_events; static std::vector<const CanEvent *> new_events;
new_events_map.clear();
std::for_each(msg_events.begin(), msg_events.end(), [](auto &e) { e.second.clear(); });
new_events.clear(); new_events.clear();
for (auto it = first; it != last; ++it) { for (auto it = first; it != last; ++it) {
@ -156,25 +156,25 @@ void AbstractStream::mergeEvents(std::vector<Event *>::const_iterator first, std
e->size = dat.size(); e->size = dat.size();
memcpy(e->dat, (uint8_t *)dat.begin(), e->size); memcpy(e->dat, (uint8_t *)dat.begin(), e->size);
new_events_map[{.source = e->src, .address = e->address}].push_back(e); msg_events[{.source = e->src, .address = e->address}].push_back(e);
new_events.push_back(e); new_events.push_back(e);
} }
} }
} }
for (auto &[id, new_e] : new_events_map) { if (!new_events.empty()) {
for (auto &[id, new_e] : msg_events) {
if (!new_e.empty()) {
auto &e = events_[id]; auto &e = events_[id];
auto insert_pos = std::upper_bound(e.cbegin(), e.cend(), new_e.front()->mono_time, CompareCanEvent()); auto pos = std::upper_bound(e.cbegin(), e.cend(), new_e.front()->mono_time, CompareCanEvent());
e.insert(insert_pos, new_e.cbegin(), new_e.cend()); e.insert(pos, new_e.cbegin(), new_e.cend());
} }
if (!new_events.empty()) {
auto insert_pos = std::upper_bound(all_events_.cbegin(), all_events_.cend(), new_events.front()->mono_time, CompareCanEvent());
all_events_.insert(insert_pos, new_events.cbegin(), new_events.cend());
} }
auto pos = std::upper_bound(all_events_.cbegin(), all_events_.cend(), new_events.front()->mono_time, CompareCanEvent());
all_events_.insert(pos, new_events.cbegin(), new_events.cend());
emit eventsMerged(msg_events);
}
lastest_event_ts = all_events_.empty() ? 0 : all_events_.back()->mono_time; lastest_event_ts = all_events_.empty() ? 0 : all_events_.back()->mono_time;
emit eventsMerged();
} }
// CanData // CanData

@ -2,7 +2,6 @@
#include <array> #include <array>
#include <atomic> #include <atomic>
#include <deque>
#include <memory> #include <memory>
#include <tuple> #include <tuple>
#include <unordered_map> #include <unordered_map>
@ -52,6 +51,8 @@ struct BusConfig {
bool can_fd = false; bool can_fd = false;
}; };
typedef std::unordered_map<MessageId, std::vector<const CanEvent *>> MessageEventsMap;
class AbstractStream : public QObject { class AbstractStream : public QObject {
Q_OBJECT Q_OBJECT
@ -73,6 +74,7 @@ public:
virtual double getSpeed() { return 1; } virtual double getSpeed() { return 1; }
virtual bool isPaused() const { return false; } virtual bool isPaused() const { return false; }
virtual void pause(bool pause) {} virtual void pause(bool pause) {}
const MessageEventsMap &eventsMap() const { return events_; }
const std::vector<const CanEvent *> &allEvents() const { return all_events_; } const std::vector<const CanEvent *> &allEvents() const { return all_events_; }
const std::vector<const CanEvent *> &events(const MessageId &id) const; const std::vector<const CanEvent *> &events(const MessageId &id) const;
virtual const std::vector<std::tuple<double, double, TimelineType>> getTimeline() { return {}; } virtual const std::vector<std::tuple<double, double, TimelineType>> getTimeline() { return {}; }
@ -82,7 +84,7 @@ signals:
void resume(); void resume();
void seekedTo(double sec); void seekedTo(double sec);
void streamStarted(); void streamStarted();
void eventsMerged(); void eventsMerged(const MessageEventsMap &events_map);
void updated(); void updated();
void msgsReceived(const QHash<MessageId, CanData> *new_msgs, bool has_new_ids); void msgsReceived(const QHash<MessageId, CanData> *new_msgs, bool has_new_ids);
void sourcesUpdated(const SourceSet &s); void sourcesUpdated(const SourceSet &s);
@ -104,7 +106,7 @@ protected:
std::atomic<bool> processing = false; std::atomic<bool> processing = false;
std::unique_ptr<QHash<MessageId, CanData>> new_msgs; std::unique_ptr<QHash<MessageId, CanData>> new_msgs;
QHash<MessageId, CanData> all_msgs; QHash<MessageId, CanData> all_msgs;
std::unordered_map<MessageId, std::vector<const CanEvent *>> events_; MessageEventsMap events_;
std::vector<const CanEvent *> all_events_; std::vector<const CanEvent *> all_events_;
std::unique_ptr<MonotonicBuffer> event_buffer; std::unique_ptr<MonotonicBuffer> event_buffer;
std::mutex mutex; std::mutex mutex;

@ -19,7 +19,7 @@
// SegmentTree // SegmentTree
void SegmentTree::build(const QVector<QPointF> &arr) { void SegmentTree::build(const std::vector<QPointF> &arr) {
size = arr.size(); size = arr.size();
tree.resize(4 * size); // size of the tree is 4 times the size of the array tree.resize(4 * size); // size of the tree is 4 times the size of the array
if (size > 0) { if (size > 0) {
@ -27,7 +27,7 @@ void SegmentTree::build(const QVector<QPointF> &arr) {
} }
} }
void SegmentTree::build_tree(const QVector<QPointF> &arr, int n, int left, int right) { void SegmentTree::build_tree(const std::vector<QPointF> &arr, int n, int left, int right) {
if (left == right) { if (left == right) {
const double y = arr[left].y(); const double y = arr[left].y();
tree[n] = {y, y}; tree[n] = {y, y};

@ -57,12 +57,12 @@ enum {
class SegmentTree { class SegmentTree {
public: public:
SegmentTree() = default; SegmentTree() = default;
void build(const QVector<QPointF> &arr); void build(const std::vector<QPointF> &arr);
inline std::pair<double, double> minmax(int left, int right) const { return get_minmax(1, 0, size - 1, left, right); } inline std::pair<double, double> minmax(int left, int right) const { return get_minmax(1, 0, size - 1, left, right); }
private: private:
std::pair<double, double> get_minmax(int n, int left, int right, int range_left, int range_right) const; std::pair<double, double> get_minmax(int n, int left, int right, int range_left, int range_right) const;
void build_tree(const QVector<QPointF> &arr, int n, int left, int right); void build_tree(const std::vector<QPointF> &arr, int n, int left, int right);
std::vector<std::pair<double, double>> tree; std::vector<std::pair<double, double>> tree;
int size = 0; int size = 0;
}; };

Loading…
Cancel
Save