Moved out context for chart paint to separated structure.

This commit is contained in:
23rd 2023-09-19 13:02:50 +03:00 committed by John Preston
parent e4e85e5a39
commit a9b0464726
8 changed files with 192 additions and 320 deletions

View file

@ -923,22 +923,24 @@ void ChartWidget::setupChartArea() {
p.fillRect(r, st::boxBg); p.fillRect(r, st::boxBg);
if (!_chartData) {
return;
}
_horizontalLinesView.paintHorizontalLines(p, chartRect); _horizontalLinesView.paintHorizontalLines(p, chartRect);
if (_chartData) { const auto context = PaintContext{
// p.setRenderHint( _chartData,
// QPainter::Antialiasing, _animationController.currentXIndices(),
// !_animationController.isFPSSlow() _animationController.currentXLimits(),
// || !_animationController.animating()); _animationController.currentHeightLimits(),
chartRect,
false,
};
{
PainterHighQualityEnabler hp(p); PainterHighQualityEnabler hp(p);
_chartView->paint( _chartView->paint(p, context);
p,
_chartData,
_animationController.currentXIndices(),
_animationController.currentXLimits(),
_animationController.currentHeightLimits(),
chartRect,
false);
} }
_horizontalLinesView.paintCaptionsToHorizontalLines(p, chartRect); _horizontalLinesView.paintCaptionsToHorizontalLines(p, chartRect);
@ -960,10 +962,7 @@ void ChartWidget::setupChartArea() {
} }
_chartView->paintSelectedXIndex( _chartView->paintSelectedXIndex(
p, p,
_chartData, context,
_animationController.currentXLimits(),
_animationController.currentHeightLimits(),
chartRect,
_details.widget->xIndex(), _details.widget->xIndex(),
detailsAlpha); detailsAlpha);
} }
@ -1069,19 +1068,18 @@ void ChartWidget::setupFooter() {
const QRect &r) { const QRect &r) {
if (_chartData) { if (_chartData) {
p.fillRect(r, st::boxBg); p.fillRect(r, st::boxBg);
// p.setRenderHint(
// QPainter::Antialiasing, auto hp = PainterHighQualityEnabler(p);
// !_animationController.isFPSSlow()
// || !_animationController.animating());
PainterHighQualityEnabler hp(p);
_chartView->paint( _chartView->paint(
p, p,
_chartData, PaintContext{
{ 0., float64(_chartData.x.size() - 1) }, _chartData,
fullXLimits, { 0., float64(_chartData.x.size() - 1) },
_animationController.currentFooterHeightLimits(), fullXLimits,
r, _animationController.currentFooterHeightLimits(),
true); r,
true,
});
} }
}); });

View file

@ -15,25 +15,24 @@ namespace Statistic {
struct Limits; struct Limits;
struct PaintContext final {
const Data::StatisticalChart &chartData;
const Limits &xIndices;
const Limits &xPercentageLimits;
const Limits &heightLimits;
const QRect ▭
bool footer = false;
};
class AbstractChartView { class AbstractChartView {
public: public:
virtual ~AbstractChartView() = default; virtual ~AbstractChartView() = default;
virtual void paint( virtual void paint(QPainter &p, const PaintContext &c) = 0;
QPainter &p,
const Data::StatisticalChart &chartData,
const Limits &xIndices,
const Limits &xPercentageLimits,
const Limits &heightLimits,
const QRect &rect,
bool footer) = 0;
virtual void paintSelectedXIndex( virtual void paintSelectedXIndex(
QPainter &p, QPainter &p,
const Data::StatisticalChart &chartData, const PaintContext &c,
const Limits &xPercentageLimits,
const Limits &heightLimits,
const QRect &rect,
int selectedXIndex, int selectedXIndex,
float64 progress) = 0; float64 progress) = 0;

View file

@ -26,20 +26,16 @@ float64 Ratio(const LinearChartView::CachedLineRatios &ratios, int id) {
void PaintChartLine( void PaintChartLine(
QPainter &p, QPainter &p,
int lineIndex, int lineIndex,
const Data::StatisticalChart &chartData, const PaintContext &c,
const Limits &xIndices,
const Limits &xPercentageLimits,
const Limits &heightLimits,
const QSize &size,
const LinearChartView::CachedLineRatios &ratios) { const LinearChartView::CachedLineRatios &ratios) {
const auto &line = chartData.lines[lineIndex]; const auto &line = c.chartData.lines[lineIndex];
auto chartPoints = QPolygonF(); auto chartPoints = QPolygonF();
const auto localStart = std::max(0, int(xIndices.min)); const auto localStart = std::max(0, int(c.xIndices.min));
const auto localEnd = std::min( const auto localEnd = std::min(
int(chartData.xPercentage.size() - 1), int(c.chartData.xPercentage.size() - 1),
int(xIndices.max)); int(c.xIndices.max));
const auto ratio = Ratio(ratios, line.id); const auto ratio = Ratio(ratios, line.id);
@ -47,12 +43,12 @@ void PaintChartLine(
if (line.y[i] < 0) { if (line.y[i] < 0) {
continue; continue;
} }
const auto xPoint = size.width() const auto xPoint = c.rect.width()
* ((chartData.xPercentage[i] - xPercentageLimits.min) * ((c.chartData.xPercentage[i] - c.xPercentageLimits.min)
/ (xPercentageLimits.max - xPercentageLimits.min)); / (c.xPercentageLimits.max - c.xPercentageLimits.min));
const auto yPercentage = (line.y[i] * ratio - heightLimits.min) const auto yPercentage = (line.y[i] * ratio - c.heightLimits.min)
/ float64(heightLimits.max - heightLimits.min); / float64(c.heightLimits.max - c.heightLimits.min);
const auto yPoint = (1. - yPercentage) * size.height(); const auto yPoint = (1. - yPercentage) * c.rect.height();
chartPoints << QPointF(xPoint, yPoint); chartPoints << QPointF(xPoint, yPoint);
} }
p.setPen(QPen(line.color, st::statisticsChartLineWidth)); p.setPen(QPen(line.color, st::statisticsChartLineWidth));
@ -68,27 +64,19 @@ LinearChartView::LinearChartView(bool isDouble)
LinearChartView::~LinearChartView() = default; LinearChartView::~LinearChartView() = default;
void LinearChartView::paint( void LinearChartView::paint(QPainter &p, const PaintContext &c) {
QPainter &p,
const Data::StatisticalChart &chartData,
const Limits &xIndices,
const Limits &xPercentageLimits,
const Limits &heightLimits,
const QRect &rect,
bool footer) {
const auto cacheToken = LinearChartView::CacheToken( const auto cacheToken = LinearChartView::CacheToken(
xIndices, c.xIndices,
xPercentageLimits, c.xPercentageLimits,
heightLimits, c.heightLimits,
rect.size()); c.rect.size());
const auto imageSize = rect.size() * style::DevicePixelRatio(); const auto imageSize = c.rect.size() * style::DevicePixelRatio();
const auto cacheScale = 1. / style::DevicePixelRatio(); const auto cacheScale = 1. / style::DevicePixelRatio();
auto &caches = (footer ? _footerCaches : _mainCaches); auto &caches = (c.footer ? _footerCaches : _mainCaches);
for (auto i = 0; i < chartData.lines.size(); i++) { for (auto i = 0; i < c.chartData.lines.size(); i++) {
const auto &line = chartData.lines[i]; const auto &line = c.chartData.lines[i];
p.setOpacity(alpha(line.id)); p.setOpacity(alpha(line.id));
if (!p.opacity()) { if (!p.opacity()) {
continue; continue;
@ -99,7 +87,7 @@ void LinearChartView::paint(
const auto isSameToken = (cache.lastToken == cacheToken); const auto isSameToken = (cache.lastToken == cacheToken);
if ((isSameToken && cache.hq) if ((isSameToken && cache.hq)
|| (p.opacity() < 1. && !isEnabled(line.id))) { || (p.opacity() < 1. && !isEnabled(line.id))) {
p.drawImage(rect.topLeft(), cache.image); p.drawImage(c.rect.topLeft(), cache.image);
continue; continue;
} }
cache.hq = isSameToken; cache.hq = isSameToken;
@ -116,15 +104,7 @@ void LinearChartView::paint(
imagePainter.scale(cacheScale, cacheScale); imagePainter.scale(cacheScale, cacheScale);
} }
PaintChartLine( PaintChartLine(imagePainter, i, c, _cachedLineRatios);
imagePainter,
i,
chartData,
xIndices,
xPercentageLimits,
heightLimits,
rect.size(),
_cachedLineRatios);
} }
if (!isSameToken) { if (!isSameToken) {
@ -133,7 +113,7 @@ void LinearChartView::paint(
Qt::IgnoreAspectRatio, Qt::IgnoreAspectRatio,
Qt::FastTransformation); Qt::FastTransformation);
} }
p.drawImage(rect.topLeft(), image); p.drawImage(c.rect.topLeft(), image);
cache.lastToken = cacheToken; cache.lastToken = cacheToken;
cache.image = std::move(image); cache.image = std::move(image);
} }
@ -141,10 +121,7 @@ void LinearChartView::paint(
void LinearChartView::paintSelectedXIndex( void LinearChartView::paintSelectedXIndex(
QPainter &p, QPainter &p,
const Data::StatisticalChart &chartData, const PaintContext &c,
const Limits &xPercentageLimits,
const Limits &heightLimits,
const QRect &rect,
int selectedXIndex, int selectedXIndex,
float64 progress) { float64 progress) {
if (selectedXIndex < 0) { if (selectedXIndex < 0) {
@ -156,36 +133,36 @@ void LinearChartView::paintSelectedXIndex(
const auto r = st::statisticsDetailsDotRadius; const auto r = st::statisticsDetailsDotRadius;
const auto i = selectedXIndex; const auto i = selectedXIndex;
const auto isSameToken = (_selectedPoints.lastXIndex == selectedXIndex) const auto isSameToken = (_selectedPoints.lastXIndex == selectedXIndex)
&& (_selectedPoints.lastHeightLimits.min == heightLimits.min) && (_selectedPoints.lastHeightLimits.min == c.heightLimits.min)
&& (_selectedPoints.lastHeightLimits.max == heightLimits.max) && (_selectedPoints.lastHeightLimits.max == c.heightLimits.max)
&& (_selectedPoints.lastXLimits.min == xPercentageLimits.min) && (_selectedPoints.lastXLimits.min == c.xPercentageLimits.min)
&& (_selectedPoints.lastXLimits.max == xPercentageLimits.max); && (_selectedPoints.lastXLimits.max == c.xPercentageLimits.max);
auto linePainted = false; auto linePainted = false;
for (const auto &line : chartData.lines) { for (const auto &line : c.chartData.lines) {
const auto lineAlpha = alpha(line.id); const auto lineAlpha = alpha(line.id);
const auto useCache = isSameToken const auto useCache = isSameToken
|| (lineAlpha < 1. && !isEnabled(line.id)); || (lineAlpha < 1. && !isEnabled(line.id));
if (!useCache) { if (!useCache) {
// Calculate. // Calculate.
const auto r = Ratio(_cachedLineRatios, line.id); const auto r = Ratio(_cachedLineRatios, line.id);
const auto xPoint = rect.width() const auto xPoint = c.rect.width()
* ((chartData.xPercentage[i] - xPercentageLimits.min) * ((c.chartData.xPercentage[i] - c.xPercentageLimits.min)
/ (xPercentageLimits.max - xPercentageLimits.min)); / (c.xPercentageLimits.max - c.xPercentageLimits.min));
const auto yPercentage = (line.y[i] * r - heightLimits.min) const auto yPercentage = (line.y[i] * r - c.heightLimits.min)
/ float64(heightLimits.max - heightLimits.min); / float64(c.heightLimits.max - c.heightLimits.min);
const auto yPoint = (1. - yPercentage) * rect.height(); const auto yPoint = (1. - yPercentage) * c.rect.height();
_selectedPoints.points[line.id] = QPointF(xPoint, yPoint) _selectedPoints.points[line.id] = QPointF(xPoint, yPoint)
+ rect.topLeft(); + c.rect.topLeft();
} }
if (!linePainted) { if (!linePainted) {
const auto lineRect = QRectF( const auto lineRect = QRectF(
rect.x() c.rect.x()
+ begin(_selectedPoints.points)->second.x() + begin(_selectedPoints.points)->second.x()
- (st::lineWidth / 2.), - (st::lineWidth / 2.),
rect.y(), c.rect.y(),
st::lineWidth, st::lineWidth,
rect.height()); c.rect.height());
p.fillRect(lineRect, st::windowSubTextFg); p.fillRect(lineRect, st::windowSubTextFg);
linePainted = true; linePainted = true;
} }
@ -196,8 +173,8 @@ void LinearChartView::paintSelectedXIndex(
p.drawEllipse(_selectedPoints.points[line.id], r, r); p.drawEllipse(_selectedPoints.points[line.id], r, r);
} }
_selectedPoints.lastXIndex = selectedXIndex; _selectedPoints.lastXIndex = selectedXIndex;
_selectedPoints.lastHeightLimits = heightLimits; _selectedPoints.lastHeightLimits = c.heightLimits;
_selectedPoints.lastXLimits = xPercentageLimits; _selectedPoints.lastXLimits = c.xPercentageLimits;
} }
int LinearChartView::findXIndexByPosition( int LinearChartView::findXIndexByPosition(

View file

@ -25,21 +25,11 @@ public:
LinearChartView(bool isDouble); LinearChartView(bool isDouble);
~LinearChartView() override final; ~LinearChartView() override final;
void paint( void paint(QPainter &p, const PaintContext &c) override;
QPainter &p,
const Data::StatisticalChart &chartData,
const Limits &xIndices,
const Limits &xPercentageLimits,
const Limits &heightLimits,
const QRect &rect,
bool footer) override;
void paintSelectedXIndex( void paintSelectedXIndex(
QPainter &p, QPainter &p,
const Data::StatisticalChart &chartData, const PaintContext &c,
const Limits &xPercentageLimits,
const Limits &heightLimits,
const QRect &rect,
int selectedXIndex, int selectedXIndex,
float64 progress) override; float64 progress) override;

View file

@ -23,76 +23,60 @@ StackChartView::StackChartView() = default;
StackChartView::~StackChartView() = default; StackChartView::~StackChartView() = default;
void StackChartView::paint( void StackChartView::paint(QPainter &p, const PaintContext &c) {
QPainter &p,
const Data::StatisticalChart &chartData,
const Limits &xIndices,
const Limits &xPercentageLimits,
const Limits &heightLimits,
const QRect &rect,
bool footer) {
constexpr auto kOffset = float64(2); constexpr auto kOffset = float64(2);
_lastPaintedXIndices = { _lastPaintedXIndices = {
float64(std::max(0., xIndices.min - kOffset)), float64(std::max(0., c.xIndices.min - kOffset)),
float64(std::min( float64(std::min(
float64(chartData.xPercentage.size() - 1), float64(c.chartData.xPercentage.size() - 1),
xIndices.max + kOffset)), c.xIndices.max + kOffset)),
}; };
StackChartView::paint( StackChartView::paintChartAndSelected(p, c);
p,
chartData,
xPercentageLimits,
heightLimits,
rect,
footer);
} }
void StackChartView::paint( void StackChartView::paintChartAndSelected(
QPainter &p, QPainter &p,
const Data::StatisticalChart &chartData, const PaintContext &c) {
const Limits &xPercentageLimits,
const Limits &heightLimits,
const QRect &rect,
bool footer) {
const auto &[localStart, localEnd] = _lastPaintedXIndices; const auto &[localStart, localEnd] = _lastPaintedXIndices;
const auto &[leftStart, w] = ComputeLeftStartAndStep( const auto &[leftStart, w] = ComputeLeftStartAndStep(
chartData, c.chartData,
xPercentageLimits, c.xPercentageLimits,
rect, c.rect,
localStart); localStart);
const auto opacity = p.opacity(); const auto opacity = p.opacity();
auto hq = PainterHighQualityEnabler(p); auto hq = PainterHighQualityEnabler(p);
auto bottoms = std::vector<float64>(localEnd - localStart + 1, -rect.y()); auto bottoms = std::vector<float64>(
localEnd - localStart + 1,
-c.rect.y());
auto selectedBottoms = std::vector<float64>(); auto selectedBottoms = std::vector<float64>();
const auto hasSelectedXIndex = !footer && (_lastSelectedXIndex >= 0); const auto hasSelectedXIndex = !c.footer && (_lastSelectedXIndex >= 0);
if (hasSelectedXIndex) { if (hasSelectedXIndex) {
selectedBottoms = std::vector<float64>(chartData.lines.size(), 0); selectedBottoms = std::vector<float64>(c.chartData.lines.size(), 0);
constexpr auto kSelectedAlpha = 0.5; constexpr auto kSelectedAlpha = 0.5;
p.setOpacity( p.setOpacity(
anim::interpolateF(1.0, kSelectedAlpha, _lastSelectedXProgress)); anim::interpolateF(1.0, kSelectedAlpha, _lastSelectedXProgress));
} }
for (auto i = 0; i < chartData.lines.size(); i++) { for (auto i = 0; i < c.chartData.lines.size(); i++) {
const auto &line = chartData.lines[i]; const auto &line = c.chartData.lines[i];
auto path = QPainterPath(); auto path = QPainterPath();
for (auto x = localStart; x <= localEnd; x++) { for (auto x = localStart; x <= localEnd; x++) {
if (line.y[x] <= 0) { if (line.y[x] <= 0) {
continue; continue;
} }
const auto xPoint = rect.width() const auto yPercentage = (line.y[x] - c.heightLimits.min)
* ((chartData.xPercentage[x] - xPercentageLimits.min) / float64(c.heightLimits.max - c.heightLimits.min);
/ (xPercentageLimits.max - xPercentageLimits.min)); const auto yPoint = yPercentage
const auto yPercentage = (line.y[x] - heightLimits.min) * c.rect.height()
/ float64(heightLimits.max - heightLimits.min); * alpha(line.id);
const auto yPoint = yPercentage * rect.height() * alpha(line.id);
const auto bottomIndex = x - localStart; const auto bottomIndex = x - localStart;
const auto column = QRectF( const auto column = QRectF(
leftStart + (x - localStart) * w, leftStart + (x - localStart) * w,
rect.height() - bottoms[bottomIndex] - yPoint, c.rect.height() - bottoms[bottomIndex] - yPoint,
w, w,
yPoint); yPoint);
if (hasSelectedXIndex && (x == _lastSelectedXIndex)) { if (hasSelectedXIndex && (x == _lastSelectedXIndex)) {
@ -110,10 +94,10 @@ void StackChartView::paint(
if (selectedBottoms[i] <= 0) { if (selectedBottoms[i] <= 0) {
continue; continue;
} }
const auto &line = chartData.lines[i]; const auto &line = c.chartData.lines[i];
const auto yPercentage = (line.y[_lastSelectedXIndex] - heightLimits.min) const auto yPercentage = (line.y[_lastSelectedXIndex] - c.heightLimits.min)
/ float64(heightLimits.max - heightLimits.min); / float64(c.heightLimits.max - c.heightLimits.min);
const auto yPoint = yPercentage * rect.height() * alpha(line.id); const auto yPoint = yPercentage * c.rect.height() * alpha(line.id);
const auto column = QRectF( const auto column = QRectF(
leftStart + (_lastSelectedXIndex - localStart) * w, leftStart + (_lastSelectedXIndex - localStart) * w,
@ -126,22 +110,13 @@ void StackChartView::paint(
void StackChartView::paintSelectedXIndex( void StackChartView::paintSelectedXIndex(
QPainter &p, QPainter &p,
const Data::StatisticalChart &chartData, const PaintContext &c,
const Limits &xPercentageLimits,
const Limits &heightLimits,
const QRect &rect,
int selectedXIndex, int selectedXIndex,
float64 progress) { float64 progress) {
_lastSelectedXIndex = selectedXIndex; _lastSelectedXIndex = selectedXIndex;
_lastSelectedXProgress = progress; _lastSelectedXProgress = progress;
[[maybe_unused]] const auto o = ScopedPainterOpacity(p, progress); [[maybe_unused]] const auto o = ScopedPainterOpacity(p, progress);
StackChartView::paint( StackChartView::paintChartAndSelected(p, c);
p,
chartData,
xPercentageLimits,
heightLimits,
rect,
false);
} }
int StackChartView::findXIndexByPosition( int StackChartView::findXIndexByPosition(

View file

@ -25,21 +25,11 @@ public:
StackChartView(); StackChartView();
~StackChartView() override final; ~StackChartView() override final;
void paint( void paint(QPainter &p, const PaintContext &c) override;
QPainter &p,
const Data::StatisticalChart &chartData,
const Limits &xIndices,
const Limits &xPercentageLimits,
const Limits &heightLimits,
const QRect &rect,
bool footer) override;
void paintSelectedXIndex( void paintSelectedXIndex(
QPainter &p, QPainter &p,
const Data::StatisticalChart &chartData, const PaintContext &c,
const Limits &xPercentageLimits,
const Limits &heightLimits,
const QRect &rect,
int selectedXIndex, int selectedXIndex,
float64 progress) override; float64 progress) override;
@ -62,13 +52,7 @@ public:
void update(float64 dt) override; void update(float64 dt) override;
private: private:
void paint( void paintChartAndSelected(QPainter &p, const PaintContext &c);
QPainter &p,
const Data::StatisticalChart &chartData,
const Limits &xPercentageLimits,
const Limits &heightLimits,
const QRect &rect,
bool footer);
struct { struct {
Limits full; Limits full;

View file

@ -48,28 +48,21 @@ StackLinearChartView::StackLinearChartView() = default;
StackLinearChartView::~StackLinearChartView() = default; StackLinearChartView::~StackLinearChartView() = default;
void StackLinearChartView::paint( void StackLinearChartView::paint(QPainter &p, const PaintContext &c) {
QPainter &p,
const Data::StatisticalChart &chartData,
const Limits &xIndices,
const Limits &xPercentageLimits,
const Limits &heightLimits,
const QRect &rect,
bool footer) {
constexpr auto kOffset = float64(2); constexpr auto kOffset = float64(2);
const auto wasXIndices = _lastPaintedXIndices; const auto wasXIndices = _lastPaintedXIndices;
_lastPaintedXIndices = { _lastPaintedXIndices = {
float64(std::max(0., xIndices.min - kOffset)), float64(std::max(0., c.xIndices.min - kOffset)),
float64(std::min( float64(std::min(
float64(chartData.xPercentage.size() - 1), float64(c.chartData.xPercentage.size() - 1),
xIndices.max + kOffset)), c.xIndices.max + kOffset)),
}; };
if ((wasXIndices.min != _lastPaintedXIndices.min) if ((wasXIndices.min != _lastPaintedXIndices.min)
|| (wasXIndices.max != _lastPaintedXIndices.max)) { || (wasXIndices.max != _lastPaintedXIndices.max)) {
const auto &[localStart, localEnd] = _lastPaintedXIndices; const auto &[localStart, localEnd] = _lastPaintedXIndices;
_cachedTransition.lines = std::vector<Transition::TransitionLine>( _cachedTransition.lines = std::vector<Transition::TransitionLine>(
chartData.lines.size(), c.chartData.lines.size(),
Transition::TransitionLine()); Transition::TransitionLine());
for (auto j = 0; j < 2; j++) { for (auto j = 0; j < 2; j++) {
@ -77,7 +70,7 @@ void StackLinearChartView::paint(
auto stackOffset = 0; auto stackOffset = 0;
auto sum = 0.; auto sum = 0.;
auto drawingLinesCount = 0; auto drawingLinesCount = 0;
for (const auto &line : chartData.lines) { for (const auto &line : c.chartData.lines) {
if (!isEnabled(line.id)) { if (!isEnabled(line.id)) {
continue; continue;
} }
@ -87,11 +80,11 @@ void StackLinearChartView::paint(
} }
} }
for (auto k = 0; k < chartData.lines.size(); k++) { for (auto k = 0; k < c.chartData.lines.size(); k++) {
auto &linePoint = (j auto &linePoint = (j
? _cachedTransition.lines[k].end ? _cachedTransition.lines[k].end
: _cachedTransition.lines[k].start); : _cachedTransition.lines[k].start);
const auto &line = chartData.lines[k]; const auto &line = c.chartData.lines[k];
if (!isEnabled(line.id)) { if (!isEnabled(line.id)) {
continue; continue;
} }
@ -99,22 +92,22 @@ void StackLinearChartView::paint(
? (line.y[i] ? alpha(line.id) : 0) ? (line.y[i] ? alpha(line.id) : 0)
: (sum ? (line.y[i] * alpha(line.id) / sum) : 0); : (sum ? (line.y[i] * alpha(line.id) / sum) : 0);
const auto xPoint = rect.width() const auto xPoint = c.rect.width()
* ((chartData.xPercentage[i] - xPercentageLimits.min) * ((c.chartData.xPercentage[i] - c.xPercentageLimits.min)
/ (xPercentageLimits.max - xPercentageLimits.min)); / (c.xPercentageLimits.max - c.xPercentageLimits.min));
const auto height = yPercentage * rect.height(); const auto height = yPercentage * c.rect.height();
const auto yPoint = rect::bottom(rect) - height - stackOffset; const auto yPoint = rect::bottom(c.rect) - height - stackOffset;
linePoint = { xPoint, yPoint }; linePoint = { xPoint, yPoint };
stackOffset += height; stackOffset += height;
} }
} }
auto sums = std::vector<float64>(); auto sums = std::vector<float64>();
sums.reserve(chartData.lines.size()); sums.reserve(c.chartData.lines.size());
auto totalSum = 0; auto totalSum = 0;
for (const auto &line : chartData.lines) { for (const auto &line : c.chartData.lines) {
auto sum = 0; auto sum = 0;
for (auto i = xIndices.min; i <= xIndices.max; i++) { for (auto i = c.xIndices.min; i <= c.xIndices.max; i++) {
sum += line.y[i]; sum += line.y[i];
} }
sum *= alpha(line.id); sum *= alpha(line.id);
@ -129,43 +122,26 @@ void StackLinearChartView::paint(
} }
} }
StackLinearChartView::paint( StackLinearChartView::paintChartOrZoomAnimation(p, c);
p,
chartData,
xPercentageLimits,
heightLimits,
rect,
footer);
} }
void StackLinearChartView::paint( void StackLinearChartView::paintChartOrZoomAnimation(
QPainter &p, QPainter &p,
const Data::StatisticalChart &chartData, const PaintContext &c) {
const Limits &xPercentageLimits,
const Limits &heightLimits,
const QRect &rect,
bool footer) {
const auto context = PaintContext{
chartData,
xPercentageLimits,
heightLimits,
rect,
footer
};
if (_transitionProgress == 1.) { if (_transitionProgress == 1.) {
if (footer) { if (c.footer) {
return paintZoomedFooter(p, context); return paintZoomedFooter(p, c);
} else { } else {
return paintZoomed(p, context); return paintZoomed(p, c);
} }
} }
const auto &[localStart, localEnd] = _lastPaintedXIndices; const auto &[localStart, localEnd] = _lastPaintedXIndices;
_skipPoints = std::vector<bool>(chartData.lines.size(), false); _skipPoints = std::vector<bool>(c.chartData.lines.size(), false);
auto paths = std::vector<QPainterPath>( auto paths = std::vector<QPainterPath>(
chartData.lines.size(), c.chartData.lines.size(),
QPainterPath()); QPainterPath());
const auto center = QPointF(rect.center()); const auto center = QPointF(c.rect.center());
const auto rotate = [&](float64 ang, const QPointF &p) { const auto rotate = [&](float64 ang, const QPointF &p) {
return QTransform() return QTransform()
@ -175,7 +151,7 @@ void StackLinearChartView::paint(
.map(p); .map(p);
}; };
const auto hasTransitionAnimation = _transitionProgress && !footer; const auto hasTransitionAnimation = _transitionProgress && !c.footer;
auto straightLineProgress = 0.; auto straightLineProgress = 0.;
auto hasEmptyPoint = false; auto hasEmptyPoint = false;
@ -188,13 +164,13 @@ void StackLinearChartView::paint(
0., 0.,
1.); 1.);
auto rectPath = QPainterPath(); auto rectPath = QPainterPath();
rectPath.addRect(rect); rectPath.addRect(c.rect);
const auto r = anim::interpolateF( const auto r = anim::interpolateF(
1., 1.,
kCircleSizeRatio, kCircleSizeRatio,
_transitionProgress); _transitionProgress);
const auto per = anim::interpolateF(0., 100., _transitionProgress); const auto per = anim::interpolateF(0., 100., _transitionProgress);
const auto side = (rect.width() / 2.) * r; const auto side = (c.rect.width() / 2.) * r;
const auto rectF = QRectF( const auto rectF = QRectF(
center - QPointF(side, side), center - QPointF(side, side),
center + QPointF(side, side)); center + QPointF(side, side));
@ -209,8 +185,8 @@ void StackLinearChartView::paint(
auto drawingLinesCount = int(0); auto drawingLinesCount = int(0);
for (auto k = 0; k < chartData.lines.size(); k++) { for (auto k = 0; k < c.chartData.lines.size(); k++) {
const auto &line = chartData.lines[k]; const auto &line = c.chartData.lines[k];
if (!isEnabled(line.id)) { if (!isEnabled(line.id)) {
continue; continue;
} }
@ -221,8 +197,8 @@ void StackLinearChartView::paint(
lastEnabled = k; lastEnabled = k;
} }
for (auto k = 0; k < chartData.lines.size(); k++) { for (auto k = 0; k < c.chartData.lines.size(); k++) {
const auto &line = chartData.lines[k]; const auto &line = c.chartData.lines[k];
const auto isLastLine = (k == lastEnabled); const auto isLastLine = (k == lastEnabled);
const auto &transitionLine = _cachedTransition.lines[k]; const auto &transitionLine = _cachedTransition.lines[k];
if (!isEnabled(line.id)) { if (!isEnabled(line.id)) {
@ -237,20 +213,20 @@ void StackLinearChartView::paint(
? float64(y[i] ? lineAlpha : 0.) ? float64(y[i] ? lineAlpha : 0.)
: float64(sum ? (y[i] * lineAlpha / sum) : 0.); : float64(sum ? (y[i] * lineAlpha / sum) : 0.);
const auto xPoint = rect.width() const auto xPoint = c.rect.width()
* ((chartData.xPercentage[i] - xPercentageLimits.min) * ((c.chartData.xPercentage[i] - c.xPercentageLimits.min)
/ (xPercentageLimits.max - xPercentageLimits.min)); / (c.xPercentageLimits.max - c.xPercentageLimits.min));
if (!yPercentage && isLastLine) { if (!yPercentage && isLastLine) {
hasEmptyPoint = true; hasEmptyPoint = true;
} }
const auto height = yPercentage * rect.height(); const auto height = yPercentage * c.rect.height();
const auto yPoint = rect::bottom(rect) - height - stackOffset; const auto yPoint = rect::bottom(c.rect) - height - stackOffset;
// startFromY[k] = yPoint; // startFromY[k] = yPoint;
auto angle = 0.; auto angle = 0.;
auto resultPoint = QPointF(xPoint, yPoint); auto resultPoint = QPointF(xPoint, yPoint);
auto pointZero = QPointF(xPoint, rect.y() + rect.height()); auto pointZero = QPointF(xPoint, c.rect.y() + c.rect.height());
// if (i == localEnd) { // if (i == localEnd) {
// endXPoint = xPoint; // endXPoint = xPoint;
// } else if (i == localStart) { // } else if (i == localStart) {
@ -289,11 +265,11 @@ void StackLinearChartView::paint(
std::max(pointZero.x(), center.x()), std::max(pointZero.x(), center.x()),
rotate(resultAngle, pointZero).y()); rotate(resultAngle, pointZero).y());
} else { } else {
const auto &xLimits = xPercentageLimits; const auto &xLimits = c.xPercentageLimits;
const auto isNextXPointAfterCenter = false const auto isNextXPointAfterCenter = false
|| center.x() < (rect.width() * ((i == localEnd) || center.x() < (c.rect.width() * ((i == localEnd)
? 1. ? 1.
: ((chartData.xPercentage[i + 1] - xLimits.min) : ((c.chartData.xPercentage[i + 1] - xLimits.min)
/ (xLimits.max - xLimits.min)))); / (xLimits.max - xLimits.min))));
if (isNextXPointAfterCenter) { if (isNextXPointAfterCenter) {
pointZero = resultPoint = QPointF() pointZero = resultPoint = QPointF()
@ -309,7 +285,7 @@ void StackLinearChartView::paint(
} }
if (i == localStart) { if (i == localStart) {
const auto bottomLeft = QPointF(rect.x(), rect::bottom(rect)); const auto bottomLeft = QPointF(c.rect.x(), rect::bottom(c.rect));
const auto local = (hasTransitionAnimation && !isLastLine) const auto local = (hasTransitionAnimation && !isLastLine)
? rotate( ? rotate(
_transitionProgress * angle _transitionProgress * angle
@ -361,10 +337,10 @@ void StackLinearChartView::paint(
|| (local.y() > center.y() || (local.y() > center.y()
&& resultPoint.y() > center.y())); && resultPoint.y() > center.y()));
const auto endQuarter = (!ending) const auto endQuarter = (!ending)
? QuarterForPoint(rect, resultPoint) ? QuarterForPoint(c.rect, resultPoint)
: kRightTop; : kRightTop;
const auto startQuarter = (!ending) const auto startQuarter = (!ending)
? QuarterForPoint(rect, local) ? QuarterForPoint(c.rect, local)
: (transitionLine.angle == -180.) : (transitionLine.angle == -180.)
? kRightTop ? kRightTop
: kLeftTop; : kLeftTop;
@ -372,14 +348,14 @@ void StackLinearChartView::paint(
for (auto q = endQuarter; q <= startQuarter; q++) { for (auto q = endQuarter; q <= startQuarter; q++) {
chartPath.lineTo( chartPath.lineTo(
(q == kLeftTop || q == kLeftBottom) (q == kLeftTop || q == kLeftBottom)
? rect.x() ? c.rect.x()
: rect::right(rect), : rect::right(c.rect),
(q == kLeftTop || q == kRightTop) (q == kLeftTop || q == kRightTop)
? rect.y() ? c.rect.y()
: rect::right(rect)); : rect::right(c.rect));
} }
} else { } else {
chartPath.lineTo(rect::right(rect), rect::bottom(rect)); chartPath.lineTo(rect::right(c.rect), rect::bottom(c.rect));
} }
} }
@ -389,23 +365,23 @@ void StackLinearChartView::paint(
auto hq = PainterHighQualityEnabler(p); auto hq = PainterHighQualityEnabler(p);
p.fillRect(rect, st::boxBg); p.fillRect(c.rect, st::boxBg);
if (!ovalPath.isEmpty()) { if (!ovalPath.isEmpty()) {
p.setClipPath(ovalPath); p.setClipPath(ovalPath);
} }
const auto opacity = footer ? (1. - _transitionProgress) : 1.; const auto opacity = c.footer ? (1. - _transitionProgress) : 1.;
for (auto k = int(chartData.lines.size() - 1); k >= 0; k--) { for (auto k = int(c.chartData.lines.size() - 1); k >= 0; k--) {
if (paths[k].isEmpty()) { if (paths[k].isEmpty()) {
continue; continue;
} }
const auto &line = chartData.lines[k]; const auto &line = c.chartData.lines[k];
p.setOpacity(alpha(line.id) * opacity); p.setOpacity(alpha(line.id) * opacity);
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
p.fillPath(paths[k], line.color); p.fillPath(paths[k], line.color);
} }
p.setOpacity(opacity); p.setOpacity(opacity);
if (!footer) { if (!c.footer) {
constexpr auto kAlphaTextPart = 0.6; constexpr auto kAlphaTextPart = 0.6;
const auto progress = std::clamp( const auto progress = std::clamp(
(_transitionProgress - kAlphaTextPart) / (1. - kAlphaTextPart), (_transitionProgress - kAlphaTextPart) / (1. - kAlphaTextPart),
@ -413,14 +389,14 @@ void StackLinearChartView::paint(
1.); 1.);
if (progress > 0) { if (progress > 0) {
auto o = ScopedPainterOpacity(p, progress); auto o = ScopedPainterOpacity(p, progress);
paintPieText(p, context); paintPieText(p, c);
} }
} else { } else {
paintZoomedFooter(p, context); paintZoomedFooter(p, c);
} }
// Fix ugly outline. // Fix ugly outline.
if (!footer || !_transitionProgress) { if (!c.footer || !_transitionProgress) {
p.setBrush(Qt::transparent); p.setBrush(Qt::transparent);
p.setPen(st::boxBg); p.setPen(st::boxBg);
p.drawPath(ovalPath); p.drawPath(ovalPath);
@ -672,10 +648,7 @@ bool StackLinearChartView::skipSelectedTranslation() const {
void StackLinearChartView::paintSelectedXIndex( void StackLinearChartView::paintSelectedXIndex(
QPainter &p, QPainter &p,
const Data::StatisticalChart &chartData, const PaintContext &c,
const Limits &xPercentageLimits,
const Limits &heightLimits,
const QRect &rect,
int selectedXIndex, int selectedXIndex,
float64 progress) { float64 progress) {
if (selectedXIndex < 0) { if (selectedXIndex < 0) {
@ -685,39 +658,39 @@ void StackLinearChartView::paintSelectedXIndex(
const auto r = st::statisticsDetailsDotRadius; const auto r = st::statisticsDetailsDotRadius;
const auto i = selectedXIndex; const auto i = selectedXIndex;
const auto isSameToken = (_selectedPoints.lastXIndex == selectedXIndex) const auto isSameToken = (_selectedPoints.lastXIndex == selectedXIndex)
&& (_selectedPoints.lastHeightLimits.min == heightLimits.min) && (_selectedPoints.lastHeightLimits.min == c.heightLimits.min)
&& (_selectedPoints.lastHeightLimits.max == heightLimits.max) && (_selectedPoints.lastHeightLimits.max == c.heightLimits.max)
&& (_selectedPoints.lastXLimits.min == xPercentageLimits.min) && (_selectedPoints.lastXLimits.min == c.xPercentageLimits.min)
&& (_selectedPoints.lastXLimits.max == xPercentageLimits.max); && (_selectedPoints.lastXLimits.max == c.xPercentageLimits.max);
for (const auto &line : chartData.lines) { for (const auto &line : c.chartData.lines) {
const auto lineAlpha = alpha(line.id); const auto lineAlpha = alpha(line.id);
const auto useCache = isSameToken const auto useCache = isSameToken
|| (lineAlpha < 1. && !isEnabled(line.id)); || (lineAlpha < 1. && !isEnabled(line.id));
if (!useCache) { if (!useCache) {
// Calculate. // Calculate.
const auto xPoint = rect.width() const auto xPoint = c.rect.width()
* ((chartData.xPercentage[i] - xPercentageLimits.min) * ((c.chartData.xPercentage[i] - c.xPercentageLimits.min)
/ (xPercentageLimits.max - xPercentageLimits.min)); / (c.xPercentageLimits.max - c.xPercentageLimits.min));
const auto yPercentage = (line.y[i] - heightLimits.min) const auto yPercentage = (line.y[i] - c.heightLimits.min)
/ float64(heightLimits.max - heightLimits.min); / float64(c.heightLimits.max - c.heightLimits.min);
_selectedPoints.points[line.id] = QPointF(xPoint, 0) _selectedPoints.points[line.id] = QPointF(xPoint, 0)
+ rect.topLeft(); + c.rect.topLeft();
} }
{ {
const auto lineRect = QRectF( const auto lineRect = QRectF(
rect.x() c.rect.x()
+ begin(_selectedPoints.points)->second.x() + begin(_selectedPoints.points)->second.x()
- (st::lineWidth / 2.), - (st::lineWidth / 2.),
rect.y(), c.rect.y(),
st::lineWidth, st::lineWidth,
rect.height()); c.rect.height());
p.fillRect(lineRect, st::windowSubTextFg); p.fillRect(lineRect, st::windowSubTextFg);
} }
} }
_selectedPoints.lastXIndex = selectedXIndex; _selectedPoints.lastXIndex = selectedXIndex;
_selectedPoints.lastHeightLimits = heightLimits; _selectedPoints.lastHeightLimits = c.heightLimits;
_selectedPoints.lastXLimits = xPercentageLimits; _selectedPoints.lastXLimits = c.xPercentageLimits;
} }
int StackLinearChartView::findXIndexByPosition( int StackLinearChartView::findXIndexByPosition(

View file

@ -26,21 +26,11 @@ public:
StackLinearChartView(); StackLinearChartView();
~StackLinearChartView() override final; ~StackLinearChartView() override final;
void paint( void paint(QPainter &p, const PaintContext &c) override;
QPainter &p,
const Data::StatisticalChart &chartData,
const Limits &xIndices,
const Limits &xPercentageLimits,
const Limits &heightLimits,
const QRect &rect,
bool footer) override;
void paintSelectedXIndex( void paintSelectedXIndex(
QPainter &p, QPainter &p,
const Data::StatisticalChart &chartData, const PaintContext &c,
const Limits &xPercentageLimits,
const Limits &heightLimits,
const QRect &rect,
int selectedXIndex, int selectedXIndex,
float64 progress) override; float64 progress) override;
@ -69,25 +59,11 @@ public:
const QPoint &p); const QPoint &p);
private: private:
struct PaintContext final { void paintChartOrZoomAnimation(QPainter &p, const PaintContext &c);
const Data::StatisticalChart &chartData;
const Limits &xPercentageLimits;
const Limits &heightLimits;
const QRect &rect;
bool footer = false;
};
void paint( void paintZoomed(QPainter &p, const PaintContext &c);
QPainter &p, void paintZoomedFooter(QPainter &p, const PaintContext &c);
const Data::StatisticalChart &chartData, void paintPieText(QPainter &p, const PaintContext &c);
const Limits &xPercentageLimits,
const Limits &heightLimits,
const QRect &rect,
bool footer);
void paintZoomed(QPainter &p, const PaintContext &context);
void paintZoomedFooter(QPainter &p, const PaintContext &context);
void paintPieText(QPainter &p, const PaintContext &context);
[[nodiscard]] bool skipSelectedTranslation() const; [[nodiscard]] bool skipSelectedTranslation() const;