subsurface/stats/statsaxis.h
Berthold Stoeger 31da037701 statistics: implement axes
Implement five kinds of axes:
 - ValueAxis: a standard axis for plotting numerical linear data.
 - CountAxis: a ValueAxis for plotting counts of dives.
 - CategoryAxis: an axis for plotting discrete variables without
   any notion of distance.
 - HistogramAxis: an axis for plotting bins with a numeric value.
 - DateAxis: a HistogramAxis that formats dates.

The axes derive from a common virtual base class that defines
a small interface, notably, returning the minimum and maximum
displayed value and redrawing the axis.

The mapping and painting is performed by QtCharts' axes. On
the one hand, using QtCharts turned out to be too inflexible.
On the other hand it allowed us to quickly prototype the charts.
Ultimately, we should do our own drawing of the axis.

As a testament to the inflexibility, QtCharts' axes do not
allow for repeated labels is needed for quarter-based date
charts (year, Q2, Q3, Q4, year, Q2, Q3, ...). Therefore the
code disambiguates labels by adding unicode zero-width spaces.
Wonderful.

When omitting labels due to space reasons, the histogram
axis attempts to show "preferred" labels. In the quarter
example above, it tries to show full years.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-02 11:04:03 -08:00

85 lines
2.2 KiB
C++

// SPDX-License-Identifier: GPL-2.0
// Supported chart axes
#ifndef STATS_AXIS_H
#define STATS_AXIS_H
#include <vector>
#include <QBarCategoryAxis>
#include <QCategoryAxis>
#include <QValueAxis>
namespace QtCharts {
class QChart;
}
class StatsAxis {
public:
virtual ~StatsAxis();
virtual void updateLabels(const QtCharts::QChart *chart) = 0;
virtual QtCharts::QAbstractAxis *qaxis() = 0;
// Returns minimum and maximum of shown range, not of data points.
virtual std::pair<double, double> minMax() const;
protected:
StatsAxis(bool horizontal);
int guessNumTicks(const QtCharts::QChart *chart, const QtCharts::QAbstractAxis *axis, const std::vector<QString> &strings) const;
bool horizontal;
};
// Small template that derives from a QChart-axis and defines
// the corresponding virtual axis() accessor.
template<typename QAxis>
class StatsAxisTemplate : public StatsAxis, public QAxis
{
using StatsAxis::StatsAxis;
QtCharts::QAbstractAxis *qaxis() override final {
return this;
}
};
class ValueAxis : public StatsAxisTemplate<QtCharts::QValueAxis> {
public:
ValueAxis(double min, double max, int decimals, bool horizontal);
private:
double min, max;
int decimals;
void updateLabels(const QtCharts::QChart *chart) override;
std::pair<double, double> minMax() const override;
};
class CountAxis : public ValueAxis {
public:
CountAxis(int count, bool horizontal);
private:
int count;
void updateLabels(const QtCharts::QChart *chart) override;
};
class CategoryAxis : public StatsAxisTemplate<QtCharts::QBarCategoryAxis> {
public:
CategoryAxis(const std::vector<QString> &labels, bool horizontal);
private:
void updateLabels(const QtCharts::QChart *chart);
};
struct HistogramAxisEntry {
QString name;
double value;
bool recommended;
};
class HistogramAxis : public StatsAxisTemplate<QtCharts::QCategoryAxis> {
public:
HistogramAxis(std::vector<HistogramAxisEntry> bin_values, bool horizontal);
private:
void updateLabels(const QtCharts::QChart *chart) override;
std::pair<double, double> minMax() const override;
std::vector<HistogramAxisEntry> bin_values;
int preferred_step;
};
class DateAxis : public HistogramAxis {
public:
DateAxis(double from, double to, bool horizontal);
};
#endif