statistics: don't place labels at half-integer values

Placing labels at half-integer values gives horrible
rendering artifacts. Therefore, always round to integer
values. The easiest way to do this is right before setting
the position. Introduce a helper function to round QPointF
in such scenarios.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2021-02-06 12:26:54 +01:00 committed by Dirk Hohndel
parent f1203d365a
commit 5b6f468547
7 changed files with 31 additions and 12 deletions

View file

@ -143,6 +143,7 @@ SOURCES += subsurface-mobile-main.cpp \
stats/statsaxis.cpp \
stats/statscolors.cpp \
stats/statsgrid.cpp \
stats/statshelper.cpp \
stats/statsseries.cpp \
stats/statsstate.cpp \
mobile-widgets/qmlinterface.cpp \
@ -295,6 +296,7 @@ HEADERS += \
stats/statsaxis.h \
stats/statscolors.h \
stats/statsgrid.h \
stats/statshelper.h \
stats/statsseries.h \
stats/statsstate.h \
stats/statstranslations.h \

View file

@ -34,6 +34,8 @@ set(SUBSURFACE_STATS_SRCS
statscolors.cpp
statsgrid.h
statsgrid.cpp
statshelper.h
statshelper.cpp
statsseries.h
statsseries.cpp
statsstate.h

View file

@ -118,7 +118,7 @@ void BarSeries::BarLabel::updatePosition(bool horizontal, bool center, const QRe
return;
}
QPointF pos = rect.center();
pos.rx() -= round(itemSize.width() / 2.0);
pos.rx() -= itemSize.width() / 2.0;
// Heuristics: if the label fits nicely into the bar (bar height is at least twice the label height),
// then put the label in the middle of the bar. Otherwise, put it at the top of the bar.
@ -130,29 +130,29 @@ void BarSeries::BarLabel::updatePosition(bool horizontal, bool center, const QRe
setVisible(false);
return;
}
pos.ry() -= round(itemSize.height() / 2.0);
pos.ry() -= itemSize.height() / 2.0;
}
item->setPos(pos);
item->setPos(roundPos(pos)); // Round to integer to avoid ugly artifacts.
} else {
if (itemSize.height() > rect.height()) {
setVisible(false);
return;
}
QPointF pos = rect.center();
pos.ry() -= round(itemSize.height() / 2.0);
pos.ry() -= itemSize.height() / 2.0;
// Heuristics: if the label fits nicely into the bar (bar width is at least twice the label height),
// then put the label in the middle of the bar. Otherwise, put it to the right of the bar.
isOutside = !center && rect.width() < 2.0 * itemSize.width();
if (isOutside) {
pos.rx() = round(rect.right() + 2.0); // Leave two pixels(?) space
pos.rx() = rect.right() + 2.0; // Leave two pixels(?) space
} else {
if (itemSize.width() > rect.width()) {
setVisible(false);
return;
}
}
item->setPos(pos);
item->setPos(roundPos(pos)); // Round to integer to avoid ugly artifacts.
}
setVisible(true);
// If label changed from inside to outside, or vice-versa, the color might change.

View file

@ -44,8 +44,9 @@ void PieSeries::Item::updatePositions(const QPointF &center, double radius)
// because half-integer values gives horrible aliasing artifacts.
if (innerLabel) {
QRectF labelRect = innerLabel->getRect();
innerLabel->setPos(QPointF(round(center.x() + innerLabelPos.x() * radius - labelRect.width() / 2.0),
round(center.y() + innerLabelPos.y() * radius - labelRect.height() / 2.0)));
QPointF pos(center.x() + innerLabelPos.x() * radius - labelRect.width() / 2.0,
center.y() + innerLabelPos.y() * radius - labelRect.height() / 2.0);
innerLabel->setPos(roundPos(pos));
}
if (outerLabel) {
QRectF labelRect = outerLabel->getRect();
@ -59,7 +60,7 @@ void PieSeries::Item::updatePositions(const QPointF &center, double radius)
pos.ry() -= labelRect.height();
}
outerLabel->setPos(QPointF(round(pos.x()), round(pos.y())));
outerLabel->setPos(roundPos(pos));
}
}

10
stats/statshelper.cpp Normal file
View file

@ -0,0 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
#include "statshelper.h"
#include <cmath>
QPointF roundPos(const QPointF &p)
{
return QPointF(round(p.x()), round(p.y()));
}

View file

@ -1,12 +1,16 @@
// SPDX-License-Identifier: GPL-2.0
// Helper functions to render the stats. Currently contains
// Helper functions to render the stats. Includes
// QSGNode template jugglery to overcome API flaws.
#ifndef STATSHELPER_H
#define STATSHELPER_H
#include <memory>
#include <QPointF>
#include <QSGNode>
// Round positions to integer values to avoid ugly artifacts
QPointF roundPos(const QPointF &p);
// A stupid pointer class that initializes to null and can be copy
// assigned. This is for historical reasons: unique_ptrs to ChartItems
// were replaced by plain pointers. Instead of nulling the plain pointers

View file

@ -397,8 +397,8 @@ void StatsView::updateTitlePos()
{
if (!title)
return;
title->setPos(QPointF(round(sceneBorder + (boundingRect().width() - title->getRect().width()) / 2.0),
round(sceneBorder)));
QPointF pos(sceneBorder + (boundingRect().width() - title->getRect().width()) / 2.0, sceneBorder);
title->setPos(roundPos(pos));
}
template <typename T, class... Args>