summaryrefslogtreecommitdiffstats
path: root/stats/statsview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'stats/statsview.cpp')
-rw-r--r--stats/statsview.cpp154
1 files changed, 81 insertions, 73 deletions
diff --git a/stats/statsview.cpp b/stats/statsview.cpp
index 5d7183fc3..e5cd0f8be 100644
--- a/stats/statsview.cpp
+++ b/stats/statsview.cpp
@@ -80,13 +80,17 @@ void StatsView::mouseReleaseEvent(QMouseEvent *)
}
}
+// Define a hideable dummy QSG node that is used as a parent node to make
+// all objects of a z-level visible / invisible.
+using ZNode = HideableQSGNode<QSGNode>;
+
class RootNode : public QSGNode
{
public:
RootNode(QQuickWindow *w);
std::unique_ptr<QSGRectangleNode> backgroundNode; // solid background
// We entertain one node per Z-level.
- std::array<std::unique_ptr<QSGNode>, (size_t)ChartZValue::Count> zNodes;
+ std::array<std::unique_ptr<ZNode>, (size_t)ChartZValue::Count> zNodes;
};
RootNode::RootNode(QQuickWindow *w)
@@ -99,7 +103,7 @@ RootNode::RootNode(QQuickWindow *w)
appendChildNode(backgroundNode.get());
for (auto &zNode: zNodes) {
- zNode.reset(new QSGNode);
+ zNode.reset(new ZNode(true));
appendChildNode(zNode.get());
}
}
@@ -268,8 +272,10 @@ void StatsView::plotAreaChanged(const QSizeF &s)
marker->updatePosition();
if (regressionItem)
regressionItem->updatePosition();
- for (auto &marker: histogramMarkers)
- marker->updatePosition();
+ if (meanMarker)
+ meanMarker->updatePosition();
+ if (medianMarker)
+ medianMarker->updatePosition();
if (legend)
legend->resize();
updateTitlePos();
@@ -369,6 +375,8 @@ void StatsView::reset()
title.reset();
legend.reset();
regressionItem.reset();
+ meanMarker.reset();
+ medianMarker.reset();
// Mark clean and dirty chart items for deletion
cleanItems.splice(deletedItems);
@@ -376,7 +384,6 @@ void StatsView::reset()
series.clear();
quartileMarkers.clear();
- histogramMarkers.clear();
grid.reset();
}
@@ -384,10 +391,18 @@ void StatsView::plot(const StatsState &stateIn)
{
state = stateIn;
plotChart();
+ updateFeatures(); // Show / hide chart features, such as legend, etc.
plotAreaChanged(boundingRect().size());
update();
}
+void StatsView::updateFeatures(const StatsState &stateIn)
+{
+ state = stateIn;
+ updateFeatures();
+ update();
+}
+
void StatsView::plotChart()
{
if (!state.var1)
@@ -398,27 +413,26 @@ void StatsView::plotChart()
switch (state.type) {
case ChartType::DiscreteBar:
return plotBarChart(dives, state.subtype, state.var1, state.var1Binner, state.var2,
- state.var2Binner, state.labels, state.legend);
+ state.var2Binner);
case ChartType::DiscreteValue:
return plotValueChart(dives, state.subtype, state.var1, state.var1Binner, state.var2,
- state.var2Operation, state.labels);
+ state.var2Operation);
case ChartType::DiscreteCount:
- return plotDiscreteCountChart(dives, state.subtype, state.var1, state.var1Binner, state.labels);
+ return plotDiscreteCountChart(dives, state.subtype, state.var1, state.var1Binner);
case ChartType::Pie:
- return plotPieChart(dives, state.var1, state.var1Binner, state.labels, state.legend);
+ return plotPieChart(dives, state.var1, state.var1Binner);
case ChartType::DiscreteBox:
return plotDiscreteBoxChart(dives, state.var1, state.var1Binner, state.var2);
case ChartType::DiscreteScatter:
- return plotDiscreteScatter(dives, state.var1, state.var1Binner, state.var2, state.quartiles);
+ return plotDiscreteScatter(dives, state.var1, state.var1Binner, state.var2);
case ChartType::HistogramCount:
- return plotHistogramCountChart(dives, state.subtype, state.var1, state.var1Binner,
- state.labels, state.median, state.mean);
+ return plotHistogramCountChart(dives, state.subtype, state.var1, state.var1Binner);
case ChartType::HistogramValue:
return plotHistogramValueChart(dives, state.subtype, state.var1, state.var1Binner, state.var2,
- state.var2Operation, state.labels);
+ state.var2Operation);
case ChartType::HistogramStacked:
return plotHistogramStackedChart(dives, state.subtype, state.var1, state.var1Binner,
- state.var2, state.var2Binner, state.labels, state.legend);
+ state.var2, state.var2Binner);
case ChartType::HistogramBox:
return plotHistogramBoxChart(dives, state.var1, state.var1Binner, state.var2);
case ChartType::ScatterPlot:
@@ -431,6 +445,25 @@ void StatsView::plotChart()
}
}
+void StatsView::updateFeatures()
+{
+ if (legend)
+ legend->setVisible(state.legend);
+
+ // For labels, we are brutal: simply show/hide the whole z-level with the labels
+ if (rootNode)
+ rootNode->zNodes[(int)ChartZValue::SeriesLabels]->setVisible(state.labels);
+
+ if (meanMarker)
+ meanMarker->setVisible(state.mean);
+
+ if (medianMarker)
+ medianMarker->setVisible(state.median);
+
+ for (ChartItemPtr<QuartileMarker> &marker: quartileMarkers)
+ marker->setVisible(state.quartiles);
+}
+
template<typename T>
CategoryAxis *StatsView::createCategoryAxis(const QString &name, const StatsBinner &binner,
const std::vector<T> &bins, bool isHorizontal)
@@ -517,14 +550,12 @@ static std::vector<QString> makePercentageLabels(int count, int total, bool isHo
// From a list of counts, make (count, label) pairs, where the label
// formats the total number and the percentage of dives.
-static std::vector<std::pair<int, std::vector<QString>>> makeCountLabels(const std::vector<int> &counts, int total,
- bool labels, bool isHorizontal)
+static std::vector<std::pair<int, std::vector<QString>>> makeCountLabels(const std::vector<int> &counts, int total, bool isHorizontal)
{
std::vector<std::pair<int, std::vector<QString>>> count_labels;
count_labels.reserve(counts.size());
for (int count: counts) {
- std::vector<QString> label = labels ? makePercentageLabels(count, total, isHorizontal)
- : std::vector<QString>();
+ std::vector<QString> label = makePercentageLabels(count, total, isHorizontal);
count_labels.push_back(std::make_pair(count, label));
}
return count_labels;
@@ -533,7 +564,7 @@ static std::vector<std::pair<int, std::vector<QString>>> makeCountLabels(const s
void StatsView::plotBarChart(const std::vector<dive *> &dives,
ChartSubType subType,
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
- const StatsVariable *valueVariable, const StatsBinner *valueBinner, bool labels, bool showLegend)
+ const StatsVariable *valueVariable, const StatsBinner *valueBinner)
{
if (!categoryBinner || !valueBinner)
return;
@@ -561,14 +592,13 @@ void StatsView::plotBarChart(const std::vector<dive *> &dives,
setAxes(catAxis, valAxis);
// Paint legend first, because the bin-names will be moved away from.
- if (showLegend)
- legend = createChartItem<Legend>(data.vbinNames);
+ legend = createChartItem<Legend>(data.vbinNames);
std::vector<BarSeries::MultiItem> items;
items.reserve(data.hbin_counts.size());
double pos = 0.0;
for (auto &[hbin, counts, total]: data.hbin_counts) {
- items.push_back({ pos - 0.5, pos + 0.5, makeCountLabels(counts, total, labels, isHorizontal),
+ items.push_back({ pos - 0.5, pos + 0.5, makeCountLabels(counts, total, isHorizontal),
categoryBinner->formatWithUnit(*hbin) });
pos += 1.0;
}
@@ -645,8 +675,7 @@ static std::pair<double, double> getMinMaxValue(const std::vector<StatsBinOp> &b
void StatsView::plotValueChart(const std::vector<dive *> &dives,
ChartSubType subType,
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
- const StatsVariable *valueVariable, StatsOperation valueAxisOperation,
- bool labels)
+ const StatsVariable *valueVariable, StatsOperation valueAxisOperation)
{
if (!categoryBinner)
return;
@@ -681,8 +710,7 @@ void StatsView::plotValueChart(const std::vector<dive *> &dives,
if (res.isValid()) {
double height = res.get(valueAxisOperation);
QString value = QString("%L1").arg(height, 0, 'f', decimals);
- std::vector<QString> label = labels ? std::vector<QString> { value }
- : std::vector<QString>();
+ std::vector<QString> label = std::vector<QString> { value };
items.push_back({ pos - 0.5, pos + 0.5, height, label,
categoryBinner->formatWithUnit(*bin), res });
}
@@ -713,8 +741,7 @@ static int getMaxCount(const std::vector<T> &bins)
void StatsView::plotDiscreteCountChart(const std::vector<dive *> &dives,
ChartSubType subType,
- const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
- bool labels)
+ const StatsVariable *categoryVariable, const StatsBinner *categoryBinner)
{
if (!categoryBinner)
return;
@@ -745,8 +772,7 @@ void StatsView::plotDiscreteCountChart(const std::vector<dive *> &dives,
items.reserve(categoryBins.size());
double pos = 0.0;
for (auto const &[bin, count]: categoryBins) {
- std::vector<QString> label = labels ? makePercentageLabels(count, total, isHorizontal)
- : std::vector<QString>();
+ std::vector<QString> label = makePercentageLabels(count, total, isHorizontal);
items.push_back({ pos - 0.5, pos + 0.5, count, label,
categoryBinner->formatWithUnit(*bin), total });
pos += 1.0;
@@ -756,8 +782,7 @@ void StatsView::plotDiscreteCountChart(const std::vector<dive *> &dives,
}
void StatsView::plotPieChart(const std::vector<dive *> &dives,
- const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
- bool labels, bool showLegend)
+ const StatsVariable *categoryVariable, const StatsBinner *categoryBinner)
{
if (!categoryBinner)
return;
@@ -776,10 +801,9 @@ void StatsView::plotPieChart(const std::vector<dive *> &dives,
data.emplace_back(categoryBinner->formatWithUnit(*bin), count);
bool keepOrder = categoryVariable->type() != StatsVariable::Type::Discrete;
- PieSeries *series = createSeries<PieSeries>(categoryVariable->name(), data, keepOrder, labels);
+ PieSeries *series = createSeries<PieSeries>(categoryVariable->name(), data, keepOrder);
- if (showLegend)
- legend = createChartItem<Legend>(series->binNames());
+ legend = createChartItem<Legend>(series->binNames());
}
void StatsView::plotDiscreteBoxChart(const std::vector<dive *> &dives,
@@ -818,7 +842,7 @@ void StatsView::plotDiscreteBoxChart(const std::vector<dive *> &dives,
void StatsView::plotDiscreteScatter(const std::vector<dive *> &dives,
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
- const StatsVariable *valueVariable, bool quartiles)
+ const StatsVariable *valueVariable)
{
if (!categoryBinner)
return;
@@ -846,26 +870,19 @@ void StatsView::plotDiscreteScatter(const std::vector<dive *> &dives,
for (const auto &[bin, array]: categoryBins) {
for (auto [v, d]: array)
series->append(d, x, v);
- if (quartiles) {
- StatsQuartiles quartiles = StatsVariable::quartiles(array);
- if (quartiles.isValid()) {
- quartileMarkers.push_back(createChartItem<QuartileMarker>(
- x, quartiles.q1, catAxis, valAxis));
- quartileMarkers.push_back(createChartItem<QuartileMarker>(
- x, quartiles.q2, catAxis, valAxis));
- quartileMarkers.push_back(createChartItem<QuartileMarker>(
- x, quartiles.q3, catAxis, valAxis));
- }
+ StatsQuartiles quartiles = StatsVariable::quartiles(array);
+ if (quartiles.isValid()) {
+ quartileMarkers.push_back(createChartItem<QuartileMarker>(
+ x, quartiles.q1, catAxis, valAxis));
+ quartileMarkers.push_back(createChartItem<QuartileMarker>(
+ x, quartiles.q2, catAxis, valAxis));
+ quartileMarkers.push_back(createChartItem<QuartileMarker>(
+ x, quartiles.q3, catAxis, valAxis));
}
x += 1.0;
}
}
-void StatsView::addHistogramMarker(double pos, QColor color, bool isHorizontal, StatsAxis *xAxis, StatsAxis *yAxis)
-{
- histogramMarkers.push_back(createChartItem<HistogramMarker>(pos, isHorizontal, color, xAxis, yAxis));
-}
-
// Yikes, we get our data in different kinds of (bin, value) pairs.
// To create a category axis from this, we have to templatify the function.
template<typename T>
@@ -890,8 +907,7 @@ HistogramAxis *StatsView::createHistogramAxis(const QString &name, const StatsBi
void StatsView::plotHistogramCountChart(const std::vector<dive *> &dives,
ChartSubType subType,
- const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
- bool labels, bool showMedian, bool showMean)
+ const StatsVariable *categoryVariable, const StatsBinner *categoryBinner)
{
if (!categoryBinner)
return;
@@ -924,8 +940,7 @@ void StatsView::plotHistogramCountChart(const std::vector<dive *> &dives,
for (auto const &[bin, count]: categoryBins) {
double lowerBound = categoryBinner->lowerBoundToFloat(*bin);
double upperBound = categoryBinner->upperBoundToFloat(*bin);
- std::vector<QString> label = labels ? makePercentageLabels(count, total, isHorizontal)
- : std::vector<QString>();
+ std::vector<QString> label = makePercentageLabels(count, total, isHorizontal);
items.push_back({ lowerBound, upperBound, count, label,
categoryBinner->formatWithUnit(*bin), total });
@@ -934,24 +949,19 @@ void StatsView::plotHistogramCountChart(const std::vector<dive *> &dives,
createSeries<BarSeries>(isHorizontal, categoryVariable->name(), items);
if (categoryVariable->type() == StatsVariable::Type::Numeric) {
- if (showMean) {
- double mean = categoryVariable->mean(dives);
- if (!std::isnan(mean))
- addHistogramMarker(mean, Qt::green, isHorizontal, xAxis, yAxis);
- }
- if (showMedian) {
- double median = categoryVariable->quartiles(dives).q2;
- if (!std::isnan(median))
- addHistogramMarker(median, Qt::red, isHorizontal, xAxis, yAxis);
- }
+ double mean = categoryVariable->mean(dives);
+ if (!std::isnan(mean))
+ meanMarker = createChartItem<HistogramMarker>(mean, isHorizontal, Qt::green, xAxis, yAxis);
+ double median = categoryVariable->quartiles(dives).q2;
+ if (!std::isnan(median))
+ medianMarker = createChartItem<HistogramMarker>(median, isHorizontal, Qt::red, xAxis, yAxis);
}
}
void StatsView::plotHistogramValueChart(const std::vector<dive *> &dives,
ChartSubType subType,
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
- const StatsVariable *valueVariable, StatsOperation valueAxisOperation,
- bool labels)
+ const StatsVariable *valueVariable, StatsOperation valueAxisOperation)
{
if (!categoryBinner)
return;
@@ -990,8 +1000,7 @@ void StatsView::plotHistogramValueChart(const std::vector<dive *> &dives,
double lowerBound = categoryBinner->lowerBoundToFloat(*bin);
double upperBound = categoryBinner->upperBoundToFloat(*bin);
QString value = QString("%L1").arg(height, 0, 'f', decimals);
- std::vector<QString> label = labels ? std::vector<QString> { value }
- : std::vector<QString>();
+ std::vector<QString> label = std::vector<QString> { value };
items.push_back({ lowerBound, upperBound, height, label,
categoryBinner->formatWithUnit(*bin), res });
}
@@ -1002,7 +1011,7 @@ void StatsView::plotHistogramValueChart(const std::vector<dive *> &dives,
void StatsView::plotHistogramStackedChart(const std::vector<dive *> &dives,
ChartSubType subType,
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
- const StatsVariable *valueVariable, const StatsBinner *valueBinner, bool labels, bool showLegend)
+ const StatsVariable *valueVariable, const StatsBinner *valueBinner)
{
if (!categoryBinner || !valueBinner)
return;
@@ -1018,8 +1027,7 @@ void StatsView::plotHistogramStackedChart(const std::vector<dive *> &dives,
*categoryBinner, categoryBins, !isHorizontal);
BarPlotData data(categoryBins, *valueBinner);
- if (showLegend)
- legend = createChartItem<Legend>(data.vbinNames);
+ legend = createChartItem<Legend>(data.vbinNames);
CountAxis *valAxis = createCountAxis(data.maxCategoryCount, isHorizontal);
@@ -1034,7 +1042,7 @@ void StatsView::plotHistogramStackedChart(const std::vector<dive *> &dives,
for (auto &[hbin, counts, total]: data.hbin_counts) {
double lowerBound = categoryBinner->lowerBoundToFloat(*hbin);
double upperBound = categoryBinner->upperBoundToFloat(*hbin);
- items.push_back({ lowerBound, upperBound, makeCountLabels(counts, total, labels, isHorizontal),
+ items.push_back({ lowerBound, upperBound, makeCountLabels(counts, total, isHorizontal),
categoryBinner->formatWithUnit(*hbin) });
}