From: Jens Steinhauser Date: Thu, 3 Sep 2015 13:26:17 +0000 (+0200) Subject: Ruler: Factor out the calculation of the tick spacing X-Git-Url: http://git.code-monkey.de/?a=commitdiff_plain;h=4b0af0b67fe57e0880bcb8aa13b4fa807f3aaac0;p=pulseview.git Ruler: Factor out the calculation of the tick spacing This avoids useless recalculations of the tick spacings on every mouse pointer move and makes the function easier to test. --- diff --git a/pv/view/ruler.cpp b/pv/view/ruler.cpp index 87f5f9b..6f8e89a 100644 --- a/pv/view/ruler.cpp +++ b/pv/view/ruler.cpp @@ -27,8 +27,6 @@ #include "ruler.hpp" #include "view.hpp" -#include - using namespace Qt; using std::shared_ptr; @@ -49,6 +47,18 @@ Ruler::Ruler(View &parent) : connect(&view_, SIGNAL(hover_point_changed()), this, SLOT(hover_point_changed())); + connect(&view_, SIGNAL(offset_changed()), + this, SLOT(invalidate_tick_position_cache())); + connect(&view_, SIGNAL(scale_changed()), + this, SLOT(invalidate_tick_position_cache())); + connect(&view_, SIGNAL(tick_prefix_changed()), + this, SLOT(invalidate_tick_position_cache())); + connect(&view_, SIGNAL(tick_precision_changed()), + this, SLOT(invalidate_tick_position_cache())); + connect(&view_, SIGNAL(tick_period_changed()), + this, SLOT(invalidate_tick_position_cache())); + connect(&view_, SIGNAL(time_unit_changed()), + this, SLOT(invalidate_tick_position_cache())); } QSize Ruler::sizeHint() const @@ -85,56 +95,48 @@ shared_ptr Ruler::get_mouse_over_item(const QPoint &pt) void Ruler::paintEvent(QPaintEvent*) { - const int ValueMargin = 3; - - QPainter p(this); - p.setRenderHint(QPainter::Antialiasing); - - const double tick_period = view_.tick_period(); - - // Draw the tick marks - p.setPen(palette().color(foregroundRole())); - - const double minor_tick_period = tick_period / MinorTickSubdivision; - const pv::util::Timestamp first_major_division = - floor(view_.offset() / tick_period); - const pv::util::Timestamp first_minor_division = - ceil(view_.offset() / minor_tick_period); - const pv::util::Timestamp t0 = first_major_division * tick_period; + if (!tick_position_cache_) { + auto ffunc = [this](const pv::util::Timestamp& t) + { + return util::format_time( + t, + this->view_.tick_prefix(), + this->view_.time_unit(), + this->view_.tick_precision()); + }; + + tick_position_cache_.emplace(calculate_tick_positions( + view_.tick_period(), + view_.offset(), + view_.scale(), + width(), + ffunc)); + } - int division = (round(first_minor_division - - first_major_division * MinorTickSubdivision)).convert_to() - 1; + const int ValueMargin = 3; const int text_height = calculate_text_height(); const int ruler_height = RulerHeight * text_height; const int major_tick_y1 = text_height + ValueMargin * 2; const int minor_tick_y1 = (major_tick_y1 + ruler_height) / 2; - double x; + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); - do { - const pv::util::Timestamp t = t0 + division * minor_tick_period; - x = ((t - view_.offset()) / view_.scale()).convert_to(); + // Draw the tick marks + p.setPen(palette().color(foregroundRole())); - if (division % MinorTickSubdivision == 0) - { - // Draw a major tick - p.drawText(x, ValueMargin, 0, text_height, - AlignCenter | AlignTop | TextDontClip, - util::format_time(t, view_.tick_prefix(), view_.time_unit(), - view_.tick_precision())); - p.drawLine(QPointF(x, major_tick_y1), - QPointF(x, ruler_height)); - } - else - { - // Draw a minor tick - p.drawLine(QPointF(x, minor_tick_y1), - QPointF(x, ruler_height)); - } + for (const auto& tick: tick_position_cache_->major) { + p.drawText(tick.first, ValueMargin, 0, text_height, + AlignCenter | AlignTop | TextDontClip, tick.second); + p.drawLine(QPointF(tick.first, major_tick_y1), + QPointF(tick.first, ruler_height)); + } - division++; - } while (x < width()); + for (const auto& tick: tick_position_cache_->minor) { + p.drawLine(QPointF(tick, minor_tick_y1), + QPointF(tick, ruler_height)); + } // Draw the hover mark draw_hover_mark(p, text_height); @@ -153,6 +155,41 @@ void Ruler::paintEvent(QPaintEvent*) } } +Ruler::TickPositions Ruler::calculate_tick_positions( + const double major_period, + const pv::util::Timestamp& offset, + const double scale, + const int width, + std::function format_function) +{ + TickPositions tp; + + const double minor_period = major_period / MinorTickSubdivision; + const pv::util::Timestamp first_major_division = floor(offset / major_period); + const pv::util::Timestamp first_minor_division = ceil(offset / minor_period); + const pv::util::Timestamp t0 = first_major_division * major_period; + + int division = (round(first_minor_division - + first_major_division * MinorTickSubdivision)).convert_to() - 1; + + double x; + + do { + const pv::util::Timestamp t = t0 + division * minor_period; + x = ((t - offset) / scale).convert_to(); + + if (division % MinorTickSubdivision == 0) { + tp.major.emplace_back(x, format_function(t)); + } else { + tp.minor.emplace_back(x); + } + + division++; + } while (x < width); + + return tp; +} + void Ruler::mouseDoubleClickEvent(QMouseEvent *e) { view_.add_flag(view_.offset() + ((double)e->x() + 0.5) * view_.scale()); @@ -188,5 +225,16 @@ void Ruler::hover_point_changed() update(); } +void Ruler::invalidate_tick_position_cache() +{ + tick_position_cache_ = boost::none; +} + +void Ruler::resizeEvent(QResizeEvent*) +{ + // the tick calculation depends on the width of this widget + invalidate_tick_position_cache(); +} + } // namespace view } // namespace pv diff --git a/pv/view/ruler.hpp b/pv/view/ruler.hpp index 50bbf2c..d01b151 100644 --- a/pv/view/ruler.hpp +++ b/pv/view/ruler.hpp @@ -21,9 +21,13 @@ #ifndef PULSEVIEW_PV_VIEW_RULER_HPP #define PULSEVIEW_PV_VIEW_RULER_HPP +#include #include +#include + #include "marginwidget.hpp" +#include namespace pv { namespace view { @@ -78,7 +82,6 @@ private: void mouseDoubleClickEvent(QMouseEvent *e); -private: /** * Draw a hover arrow under the cursor position. * @param p The painter to draw into. @@ -88,8 +91,45 @@ private: int calculate_text_height() const; + struct TickPositions + { + std::vector> major; + std::vector minor; + }; + + /** + * Holds the tick positions so that they don't have to be recalculated on + * every redraw. Set by 'paintEvent()' when needed. + */ + boost::optional tick_position_cache_; + + /** + * Calculates the major and minor tick positions. + * + * @param major_period The period between the major ticks. + * @param offset The time at the left border of the ruler. + * @param scale The scale in seconds per pixel. + * @param width the Width of the ruler. + * @param format_function A function used to format the major tick times. + * @return An object of type 'TickPositions' that contains the major tick + * positions together with the labels at that ticks, and the minor + * tick positions. + */ + static TickPositions calculate_tick_positions( + const double major_period, + const pv::util::Timestamp& offset, + const double scale, + const int width, + std::function format_function); + +protected: + void resizeEvent(QResizeEvent*) override; + private Q_SLOTS: void hover_point_changed(); + + // Resets the 'tick_position_cache_'. + void invalidate_tick_position_cache(); }; } // namespace view diff --git a/pv/view/view.cpp b/pv/view/view.cpp index dbae983..fd8781d 100644 --- a/pv/view/view.cpp +++ b/pv/view/view.cpp @@ -211,11 +211,27 @@ double View::scale() const return scale_; } +void View::set_scale(double scale) +{ + if (scale_ != scale) { + scale_ = scale; + Q_EMIT scale_changed(); + } +} + const Timestamp& View::offset() const { return offset_; } +void View::set_offset(const pv::util::Timestamp& offset) +{ + if (offset_ != offset) { + offset_ = offset; + Q_EMIT offset_changed(); + } +} + int View::owner_visual_v_offset() const { return -verticalScrollBar()->sliderPosition(); @@ -238,21 +254,53 @@ pv::util::SIPrefix View::tick_prefix() const return tick_prefix_; } +void View::set_tick_prefix(pv::util::SIPrefix tick_prefix) +{ + if (tick_prefix_ != tick_prefix) { + tick_prefix_ = tick_prefix; + Q_EMIT tick_prefix_changed(); + } +} + unsigned int View::tick_precision() const { return tick_precision_; } +void View::set_tick_precision(unsigned tick_precision) +{ + if (tick_precision_ != tick_precision) { + tick_precision_ = tick_precision; + Q_EMIT tick_precision_changed(); + } +} + double View::tick_period() const { return tick_period_; } +void View::set_tick_period(double tick_period) +{ + if (tick_period_ != tick_period) { + tick_period_ = tick_period; + Q_EMIT tick_period_changed(); + } +} + TimeUnit View::time_unit() const { return time_unit_; } +void View::set_time_unit(pv::util::TimeUnit time_unit) +{ + if (time_unit_ != time_unit) { + time_unit_ = time_unit; + Q_EMIT time_unit_changed(); + } +} + void View::zoom(double steps) { zoom(steps, viewport_->width() / 2); @@ -335,15 +383,14 @@ void View::set_scale_offset(double scale, const Timestamp& offset) } } - scale_ = scale; - offset_ = offset; + set_scale(scale); + set_offset(offset); calculate_tick_spacing(); update_scroll(); ruler_->update(); viewport_->update(); - scale_offset_changed(); } set< shared_ptr > View::get_visible_data() const @@ -528,13 +575,13 @@ void View::calculate_tick_spacing() tp_with_margin = order_decimal * (ScaleUnits[unit++] + tp_margin); } while (tp_with_margin < min_period && unit < countof(ScaleUnits)); - tick_period_ = order_decimal * ScaleUnits[unit - 1]; - tick_prefix_ = static_cast( - (order - pv::util::exponent(pv::util::SIPrefix::yocto)) / 3); + set_tick_period(order_decimal * ScaleUnits[unit - 1]); + set_tick_prefix(static_cast( + (order - pv::util::exponent(pv::util::SIPrefix::yocto)) / 3)); // Precision is the number of fractional digits required, not // taking the prefix into account (and it must never be negative) - tick_precision_ = std::max((int)ceil(log10f(1 / tick_period_)), 0); + set_tick_precision(std::max((int)ceil(log10f(1 / tick_period_)), 0)); tick_period_width = tick_period_ / scale_; @@ -692,7 +739,7 @@ void View::determine_time_unit() const vector< shared_ptr > segments = data->segments(); if (!segments.empty()) if (segments[0]->samplerate()) { - time_unit_ = util::TimeUnit::Time; + set_time_unit(util::TimeUnit::Time); break; } } @@ -786,12 +833,12 @@ void View::h_scroll_value_changed(int value) const int range = horizontalScrollBar()->maximum(); if (range < MaxScrollValue) - offset_ = scale_ * value; + set_offset(scale_ * value); else { double length = 0; Timestamp offset; get_scroll_layout(length, offset); - offset_ = scale_ * length * value / MaxScrollValue; + set_offset(scale_ * length * value / MaxScrollValue); } ruler_->update(); @@ -933,7 +980,7 @@ void View::signals_changed() void View::capture_state_updated(int state) { if (state == Session::Running) - time_unit_ = util::TimeUnit::Samples; + set_time_unit(util::TimeUnit::Samples); if (state == Session::Stopped) { // After acquisition has stopped we need to re-calculate the ticks once @@ -975,7 +1022,7 @@ void View::perform_delayed_view_update() const QSize areaSize = viewport_->size(); length = max(length - areaSize.width(), 0.0); - offset_ = scale_ * length; + set_offset(scale_ * length); } determine_time_unit(); diff --git a/pv/view/view.hpp b/pv/view/view.hpp index ed1c926..90a9b87 100644 --- a/pv/view/view.hpp +++ b/pv/view/view.hpp @@ -216,12 +216,28 @@ Q_SIGNALS: void selection_changed(); - void scale_offset_changed(); + /// Emitted when the offset changed. + void offset_changed(); + + /// Emitted when the scale changed. + void scale_changed(); void sticky_scrolling_changed(bool state); void always_zoom_to_fit_changed(bool state); + /// Emitted when the tick_prefix changed. + void tick_prefix_changed(); + + /// Emitted when the tick_precision changed. + void tick_precision_changed(); + + /// Emitted when the tick_period changed. + void tick_period_changed(); + + /// Emitted when the time_unit changed. + void time_unit_changed(); + private: void get_scroll_layout(double &length, pv::util::Timestamp &offset) const; @@ -272,7 +288,6 @@ private: void determine_time_unit(); -private: bool eventFilter(QObject *object, QEvent *event); bool viewportEvent(QEvent *e); @@ -300,6 +315,42 @@ private Q_SLOTS: void on_hover_point_changed(); + /** + * Sets the 'offset_' member and emits the 'offset_changed' + * signal if needed. + */ + void set_offset(const pv::util::Timestamp& offset); + + /** + * Sets the 'scale_' member and emits the 'scale_changed' + * signal if needed. + */ + void set_scale(double scale); + + /** + * Sets the 'tick_prefix_' member and emits the 'tick_prefix_changed' + * signal if needed. + */ + void set_tick_prefix(pv::util::SIPrefix tick_prefix); + + /** + * Sets the 'tick_precision_' member and emits the 'tick_precision_changed' + * signal if needed. + */ + void set_tick_precision(unsigned tick_precision); + + /** + * Sets the 'tick_period_' member and emits the 'tick_period_changed' + * signal if needed. + */ + void set_tick_period(double tick_period); + + /** + * Sets the 'time_unit' member and emits the 'time_unit_changed' + * signal if needed. + */ + void set_time_unit(pv::util::TimeUnit time_unit); + private: Session &session_;