From cb4c6291780060bbf2552cafb42f10aae718e292 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 29 Sep 2023 17:21:53 +0300 Subject: [PATCH] Slightly refactored code for statistical charts. --- .../data/data_statistics_chart.cpp | 12 +++--- .../SourceFiles/statistics/chart_widget.cpp | 31 ++------------ .../SourceFiles/statistics/chart_widget.h | 1 - .../SourceFiles/statistics/segment_tree.cpp | 2 +- .../statistics/statistics_common.h | 15 ------- .../statistics/view/linear_chart_view.cpp | 4 +- .../view/stack_linear_chart_view.cpp | 42 ++++++++++++------- 7 files changed, 41 insertions(+), 66 deletions(-) diff --git a/Telegram/SourceFiles/data/data_statistics_chart.cpp b/Telegram/SourceFiles/data/data_statistics_chart.cpp index 5e8cec831..830f5ecc6 100644 --- a/Telegram/SourceFiles/data/data_statistics_chart.cpp +++ b/Telegram/SourceFiles/data/data_statistics_chart.cpp @@ -29,14 +29,14 @@ void StatisticalChart::measure() { } } - for (auto i = 0; i < lines.size(); i++) { - if (lines[i].maxValue > maxValue) { - maxValue = lines[i].maxValue; + for (auto &line : lines) { + if (line.maxValue > maxValue) { + maxValue = line.maxValue; } - if (lines[i].minValue < minValue) { - minValue = lines[i].minValue; + if (line.minValue < minValue) { + minValue = line.minValue; } - lines[i].segmentTree = Statistic::SegmentTree(lines[i].y); + line.segmentTree = Statistic::SegmentTree(line.y); } daysLookup.clear(); diff --git a/Telegram/SourceFiles/statistics/chart_widget.cpp b/Telegram/SourceFiles/statistics/chart_widget.cpp index 8643d8d20..ad4b59ef6 100644 --- a/Telegram/SourceFiles/statistics/chart_widget.cpp +++ b/Telegram/SourceFiles/statistics/chart_widget.cpp @@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "statistics/chart_widget.h" -#include "ui/effects/show_animation.h" #include "base/qt/qt_key_modifiers.h" #include "lang/lang_keys.h" #include "statistics/chart_header_widget.h" @@ -19,14 +18,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "statistics/view/stack_chart_common.h" #include "ui/abstract_button.h" #include "ui/effects/animation_value_f.h" +#include "ui/effects/ripple_animation.h" +#include "ui/effects/show_animation.h" +#include "ui/image/image_prepare.h" #include "ui/painter.h" #include "ui/rect.h" -#include "ui/round_rect.h" -#include "ui/effects/ripple_animation.h" -#include "ui/image/image_prepare.h" #include "ui/widgets/buttons.h" #include "styles/style_layers.h" -#include "styles/style_boxes.h" #include "styles/style_statistics.h" namespace Statistic { @@ -34,7 +32,6 @@ namespace Statistic { namespace { constexpr auto kHeightLimitsUpdateTimeout = crl::time(320); -constexpr auto kExpandingDelay = crl::time(1); inline float64 InterpolationRatio(float64 from, float64 to, float64 result) { return (result - from) / (to - from); @@ -78,10 +75,6 @@ void FillLineColorsByKey(Data::StatisticalChart &chartData) { + chartData.getDayString(limits.max); } -[[nodiscard]] bool IsChartHasLocalZoom(ChartViewType type) { - return type == ChartViewType::StackLinear; -} - void PaintBottomLine( QPainter &p, const std::vector &dates, @@ -213,13 +206,12 @@ class ChartWidget::Footer final : public RpMouseWidget { public: using PaintCallback = Fn; - Footer(not_null parent); + explicit Footer(not_null parent); void setXPercentageLimits(const Limits &xLimits); [[nodiscard]] Limits xPercentageLimits() const; [[nodiscard]] rpl::producer xPercentageLimitsChange() const; - [[nodiscard]] rpl::producer<> userInteractionFinished() const; void setPaintChartCallback(PaintCallback paintChartCallback); @@ -228,7 +220,6 @@ protected: private: rpl::event_stream _xPercentageLimitsChange; - rpl::event_stream<> _userInteractionFinished; void prepareCache(int height); @@ -338,7 +329,6 @@ ChartWidget::Footer::Footer(not_null parent) case QEvent::MouseButtonRelease: { const auto finish = [=] { _dragArea = DragArea::None; - _userInteractionFinished.fire({}); fire(); }; if ((_dragArea == DragArea::None) && !_draggedAfterPress) { @@ -456,11 +446,6 @@ void ChartWidget::Footer::paintEvent(QPaintEvent *e) { const auto innerMargins = QMargins{ 0, lineWidth, 0, lineWidth }; const auto r = rect(); const auto innerRect = r - innerMargins; - const auto inactiveLeftRect = Rect(QSizeF(_leftSide.max, r.height())) - - innerMargins; - const auto inactiveRightRect = r - - QMarginsF{ _rightSide.min, 0, 0, 0 } - - innerMargins; const auto &inactiveColor = st::statisticsChartInactive; _frame.fill(Qt::transparent); @@ -524,10 +509,6 @@ rpl::producer ChartWidget::Footer::xPercentageLimitsChange() const { return _xPercentageLimitsChange.events(); } -rpl::producer<> ChartWidget::Footer::userInteractionFinished() const { - return _userInteractionFinished.events(); -} - ChartWidget::ChartAnimationController::ChartAnimationController( Fn &&updateCallback) : _animation(std::move(updateCallback)) { @@ -830,10 +811,6 @@ bool ChartWidget::ChartAnimationController::footerAnimating() const { != _animationValueFooterHeightMax.to()); } -bool ChartWidget::ChartAnimationController::isFPSSlow() const { - return _benchmark.lastFPSSlow; -} - ChartWidget::ChartWidget(not_null parent) : Ui::RpWidget(parent) , _chartArea(base::make_unique_q(this)) diff --git a/Telegram/SourceFiles/statistics/chart_widget.h b/Telegram/SourceFiles/statistics/chart_widget.h index 12be7952d..325666e54 100644 --- a/Telegram/SourceFiles/statistics/chart_widget.h +++ b/Telegram/SourceFiles/statistics/chart_widget.h @@ -83,7 +83,6 @@ private: [[nodiscard]] Limits finalHeightLimits() const; [[nodiscard]] bool animating() const; [[nodiscard]] bool footerAnimating() const; - [[nodiscard]] bool isFPSSlow() const; [[nodiscard]] rpl::producer<> addHorizontalLineRequests() const; diff --git a/Telegram/SourceFiles/statistics/segment_tree.cpp b/Telegram/SourceFiles/statistics/segment_tree.cpp index 385560964..0032f7adc 100644 --- a/Telegram/SourceFiles/statistics/segment_tree.cpp +++ b/Telegram/SourceFiles/statistics/segment_tree.cpp @@ -37,7 +37,7 @@ void SegmentTree::build(int v, int from, int size) { _heap[v].max = _array[from]; _heap[v].min = _array[from]; } else { - // Build childs. + // Build children. build(2 * v, from, size / 2); build(2 * v + 1, from + size / 2, size - size / 2); diff --git a/Telegram/SourceFiles/statistics/statistics_common.h b/Telegram/SourceFiles/statistics/statistics_common.h index 87aa52730..e5ea79962 100644 --- a/Telegram/SourceFiles/statistics/statistics_common.h +++ b/Telegram/SourceFiles/statistics/statistics_common.h @@ -21,19 +21,4 @@ enum class ChartViewType { StackLinear, }; -[[nodiscard]] inline Limits FindNearestElements( - const std::vector &vector, - const Limits &limit) { - const auto find = [&](float64 raw) -> float64 { - const auto it = ranges::lower_bound(vector, raw); - const auto left = raw - (*(it - 1)); - const auto right = (*it) - raw; - const auto nearestXPercentageIt = ((right) > (left)) ? (it - 1) : it; - return std::distance( - begin(vector), - nearestXPercentageIt); - }; - return { find(limit.min), find(limit.max) }; -} - } // namespace Statistic diff --git a/Telegram/SourceFiles/statistics/view/linear_chart_view.cpp b/Telegram/SourceFiles/statistics/view/linear_chart_view.cpp index acf0082bd..1a952c444 100644 --- a/Telegram/SourceFiles/statistics/view/linear_chart_view.cpp +++ b/Telegram/SourceFiles/statistics/view/linear_chart_view.cpp @@ -18,7 +18,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Statistic { namespace { -float64 Ratio(const LinearChartView::CachedLineRatios &ratios, int id) { +[[nodiscard]] float64 Ratio( + const LinearChartView::CachedLineRatios &ratios, + int id) { return (id == 1) ? ratios.first : ratios.second; } diff --git a/Telegram/SourceFiles/statistics/view/stack_linear_chart_view.cpp b/Telegram/SourceFiles/statistics/view/stack_linear_chart_view.cpp index 7e61fca02..86a56e383 100644 --- a/Telegram/SourceFiles/statistics/view/stack_linear_chart_view.cpp +++ b/Telegram/SourceFiles/statistics/view/stack_linear_chart_view.cpp @@ -218,7 +218,7 @@ auto StackLinearChartView::partsPercentage( _pieHasSinglePart = false; for (auto k = 0; k < sums.size(); k++) { - const auto rawPercentage = sums[k] / totalSum; + const auto rawPercentage = totalSum ? (sums[k] / totalSum) : 0.; const auto rounded = 0.01 * std::round(rawPercentage * 100.); roundedPercentagesSum += rounded; const auto diff = rawPercentage - rounded; @@ -342,13 +342,16 @@ void StackLinearChartView::paintChartOrZoomAnimation( for (auto k = 0; k < c.chartData.lines.size(); k++) { const auto &line = c.chartData.lines[k]; const auto isLastLine = (k == lastEnabled); - const auto &transitionLine = hasTransitionAnimation - ? _transition.lines[k] - : Transition::TransitionLine(); const auto lineAlpha = linesFilter->alpha(line.id); + if (isLastLine && (lineAlpha < 1.)) { + hasEmptyPoint = true; + } if (!lineAlpha) { continue; } + const auto &transitionLine = hasTransitionAnimation + ? _transition.lines[k] + : Transition::TransitionLine(); const auto &y = line.y; auto &chartPath = paths[k]; @@ -357,7 +360,7 @@ void StackLinearChartView::paintChartOrZoomAnimation( ? float64(y[i] ? lineAlpha : 0.) : float64(sum ? (y[i] * lineAlpha / sum) : 0.); - if (!yPercentage && isLastLine) { + if (isLastLine && !yPercentage) { hasEmptyPoint = true; } const auto height = yPercentage * c.rect.height(); @@ -425,7 +428,9 @@ void StackLinearChartView::paintChartOrZoomAnimation( } if (i == localStart) { - const auto bottomLeft = QPointF(c.rect.x(), rect::bottom(c.rect)); + const auto bottomLeft = QPointF( + c.rect.x(), + rect::bottom(c.rect)); const auto local = (hasTransitionAnimation && !isLastLine) ? rotate( _transition.progress * angle @@ -495,7 +500,9 @@ void StackLinearChartView::paintChartOrZoomAnimation( : rect::right(c.rect)); } } else { - chartPath.lineTo(rect::right(c.rect), rect::bottom(c.rect)); + chartPath.lineTo( + rect::right(c.rect), + rect::bottom(c.rect)); } } @@ -510,6 +517,10 @@ void StackLinearChartView::paintChartOrZoomAnimation( p.setClipPath(ovalPath); } + if (hasEmptyPoint) { + p.fillRect(c.rect, st::boxDividerBg); + } + const auto opacity = c.footer ? (1. - _transition.progress) : 1.; for (auto k = int(c.chartData.lines.size() - 1); k >= 0; k--) { if (paths[k].isEmpty()) { @@ -708,10 +719,13 @@ void StackLinearChartView::paintPieText(QPainter &p, const PaintContext &c) { } const auto rText = side * std::sqrt(1. - percentage); - const auto textAngle = (previous + kPieAngleOffset) - + (now - previous) / 2.; + const auto textAngle = (now == previous) + ? 0. + : ((previous + kPieAngleOffset) + (now - previous) / 2.); const auto textRadians = textAngle * M_PI / 180.; - const auto scale = (minScale) + percentage * (maxScale - minScale); + const auto scale = (maxScale == minScale) + ? 0. + : (minScale) + percentage * (maxScale - minScale); const auto text = QString::number(int(percentage * 100)) + u"%"_q; const auto textW = font->width(text); const auto textH = font->height; @@ -753,7 +767,7 @@ bool StackLinearChartView::PiePartController::set(int id) { void StackLinearChartView::PiePartController::update(int id) { if (id >= 0) { const auto was = _startedAt[id]; - const auto p = (crl::now() - was) / st::slideWrapDuration; + const auto p = (crl::now() - was) / float64(st::slideWrapDuration); const auto progress = ((p > 0) && (p < 1)) ? (1. - p) : 0.; _startedAt[id] = crl::now() - (st::slideWrapDuration * progress); } @@ -804,10 +818,8 @@ void StackLinearChartView::handleMouseMove( } const auto center = rect.center(); const auto theta = std::atan2(center.y() - p.y(), (center.x() - p.x())); - const auto angle = [&] { - const auto a = theta * (180. / M_PI) + 90.; - return (a > 180.) ? (a - 360.) : a; - }(); + const auto rawAngle = theta * (180. / M_PI) + 90.; + const auto angle = (rawAngle > 180.) ? (rawAngle - 360.) : rawAngle; for (auto k = 0; k < chartData.lines.size(); k++) { const auto previous = k ? _transition.lines[k - 1].angle