mirror of
https://github.com/subsurface/subsurface.git
synced 2025-01-31 23:43:24 +00:00
statistics: don't replot chart when changing features
Up to now, when the user changed the visibility of chart features (legend, quartiles, labels, etc.) the whole chart was replot. Instead, only change the visibility status of these items. After all, this modularity is one of the things the conversion to QSG was all about. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
parent
e32e6d63a7
commit
ff536e98fc
5 changed files with 107 additions and 99 deletions
|
@ -185,7 +185,9 @@ void StatsWidget::var2OperationChanged(int idx)
|
||||||
void StatsWidget::featureChanged(int idx, bool status)
|
void StatsWidget::featureChanged(int idx, bool status)
|
||||||
{
|
{
|
||||||
state.featureChanged(idx, status);
|
state.featureChanged(idx, status);
|
||||||
updateUi();
|
// No need for a full chart replot - just show/hide the features
|
||||||
|
if (view)
|
||||||
|
view->updateFeatures(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatsWidget::showEvent(QShowEvent *e)
|
void StatsWidget::showEvent(QShowEvent *e)
|
||||||
|
|
|
@ -17,7 +17,7 @@ static const double innerLabelRadius = 0.75; // 1.0 = at outer border of pie
|
||||||
static const double outerLabelRadius = 1.01; // 1.0 = at outer border of pie
|
static const double outerLabelRadius = 1.01; // 1.0 = at outer border of pie
|
||||||
|
|
||||||
PieSeries::Item::Item(StatsView &view, const QString &name, int from, int count, int totalCount,
|
PieSeries::Item::Item(StatsView &view, const QString &name, int from, int count, int totalCount,
|
||||||
int bin_nr, int numBins, bool labels) :
|
int bin_nr, int numBins) :
|
||||||
name(name),
|
name(name),
|
||||||
count(count)
|
count(count)
|
||||||
{
|
{
|
||||||
|
@ -30,14 +30,12 @@ PieSeries::Item::Item(StatsView &view, const QString &name, int from, int count,
|
||||||
innerLabelPos = QPointF(cos(meanAngle) * innerLabelRadius, -sin(meanAngle) * innerLabelRadius);
|
innerLabelPos = QPointF(cos(meanAngle) * innerLabelRadius, -sin(meanAngle) * innerLabelRadius);
|
||||||
outerLabelPos = QPointF(cos(meanAngle) * outerLabelRadius, -sin(meanAngle) * outerLabelRadius);
|
outerLabelPos = QPointF(cos(meanAngle) * outerLabelRadius, -sin(meanAngle) * outerLabelRadius);
|
||||||
|
|
||||||
if (labels) {
|
double percentage = count * 100.0 / totalCount;
|
||||||
double percentage = count * 100.0 / totalCount;
|
QString innerLabelText = QStringLiteral("%1\%").arg(loc.toString(percentage, 'f', 1));
|
||||||
QString innerLabelText = QStringLiteral("%1\%").arg(loc.toString(percentage, 'f', 1));
|
innerLabel = view.createChartItem<ChartTextItem>(ChartZValue::SeriesLabels, f, innerLabelText);
|
||||||
innerLabel = view.createChartItem<ChartTextItem>(ChartZValue::SeriesLabels, f, innerLabelText);
|
|
||||||
|
|
||||||
outerLabel = view.createChartItem<ChartTextItem>(ChartZValue::SeriesLabels, f, name);
|
outerLabel = view.createChartItem<ChartTextItem>(ChartZValue::SeriesLabels, f, name);
|
||||||
outerLabel->setColor(darkLabelColor);
|
outerLabel->setColor(darkLabelColor);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PieSeries::Item::updatePositions(const QPointF ¢er, double radius)
|
void PieSeries::Item::updatePositions(const QPointF ¢er, double radius)
|
||||||
|
@ -75,7 +73,7 @@ void PieSeries::Item::highlight(ChartPieItem &item, int bin_nr, bool highlight,
|
||||||
}
|
}
|
||||||
|
|
||||||
PieSeries::PieSeries(StatsView &view, StatsAxis *xAxis, StatsAxis *yAxis, const QString &categoryName,
|
PieSeries::PieSeries(StatsView &view, StatsAxis *xAxis, StatsAxis *yAxis, const QString &categoryName,
|
||||||
const std::vector<std::pair<QString, int>> &data, bool keepOrder, bool labels) :
|
const std::vector<std::pair<QString, int>> &data, bool keepOrder) :
|
||||||
StatsSeries(view, xAxis, yAxis),
|
StatsSeries(view, xAxis, yAxis),
|
||||||
item(view.createChartItem<ChartPieItem>(ChartZValue::Series, pieBorderWidth)),
|
item(view.createChartItem<ChartPieItem>(ChartZValue::Series, pieBorderWidth)),
|
||||||
categoryName(categoryName),
|
categoryName(categoryName),
|
||||||
|
@ -137,7 +135,7 @@ PieSeries::PieSeries(StatsView &view, StatsAxis *xAxis, StatsAxis *yAxis, const
|
||||||
int act = 0;
|
int act = 0;
|
||||||
for (auto it2 = sorted.begin(); it2 != it; ++it2) {
|
for (auto it2 = sorted.begin(); it2 != it; ++it2) {
|
||||||
int count = data[*it2].second;
|
int count = data[*it2].second;
|
||||||
items.emplace_back(view, data[*it2].first, act, count, totalCount, (int)items.size(), numBins, labels);
|
items.emplace_back(view, data[*it2].first, act, count, totalCount, (int)items.size(), numBins);
|
||||||
act += count;
|
act += count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +145,7 @@ PieSeries::PieSeries(StatsView &view, StatsAxis *xAxis, StatsAxis *yAxis, const
|
||||||
for (auto it2 = it; it2 != sorted.end(); ++it2)
|
for (auto it2 = it; it2 != sorted.end(); ++it2)
|
||||||
other.push_back({ data[*it2].first, data[*it2].second });
|
other.push_back({ data[*it2].first, data[*it2].second });
|
||||||
QString name = StatsTranslations::tr("other (%1 items)").arg(other.size());
|
QString name = StatsTranslations::tr("other (%1 items)").arg(other.size());
|
||||||
items.emplace_back(view, name, act, totalCount - act, totalCount, (int)items.size(), numBins, labels);
|
items.emplace_back(view, name, act, totalCount - act, totalCount, (int)items.size(), numBins);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ public:
|
||||||
// If keepOrder is false, bins will be sorted by size, otherwise the sorting
|
// If keepOrder is false, bins will be sorted by size, otherwise the sorting
|
||||||
// of the shown bins will be retained. Small bins are omitted for clarity.
|
// of the shown bins will be retained. Small bins are omitted for clarity.
|
||||||
PieSeries(StatsView &view, StatsAxis *xAxis, StatsAxis *yAxis, const QString &categoryName,
|
PieSeries(StatsView &view, StatsAxis *xAxis, StatsAxis *yAxis, const QString &categoryName,
|
||||||
const std::vector<std::pair<QString, int>> &data, bool keepOrder, bool labels);
|
const std::vector<std::pair<QString, int>> &data, bool keepOrder);
|
||||||
~PieSeries();
|
~PieSeries();
|
||||||
|
|
||||||
void updatePositions() override;
|
void updatePositions() override;
|
||||||
|
@ -45,7 +45,7 @@ private:
|
||||||
int count;
|
int count;
|
||||||
QPointF innerLabelPos, outerLabelPos; // With respect to a (-1, -1)-(1, 1) rectangle.
|
QPointF innerLabelPos, outerLabelPos; // With respect to a (-1, -1)-(1, 1) rectangle.
|
||||||
Item(StatsView &view, const QString &name, int from, int count, int totalCount,
|
Item(StatsView &view, const QString &name, int from, int count, int totalCount,
|
||||||
int bin_nr, int numBins, bool labels);
|
int bin_nr, int numBins);
|
||||||
void updatePositions(const QPointF ¢er, double radius);
|
void updatePositions(const QPointF ¢er, double radius);
|
||||||
void highlight(ChartPieItem &item, int bin_nr, bool highlight, int numBins);
|
void highlight(ChartPieItem &item, int bin_nr, bool highlight, int numBins);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
class RootNode : public QSGNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RootNode(QQuickWindow *w);
|
RootNode(QQuickWindow *w);
|
||||||
std::unique_ptr<QSGRectangleNode> backgroundNode; // solid background
|
std::unique_ptr<QSGRectangleNode> backgroundNode; // solid background
|
||||||
// We entertain one node per Z-level.
|
// 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)
|
RootNode::RootNode(QQuickWindow *w)
|
||||||
|
@ -99,7 +103,7 @@ RootNode::RootNode(QQuickWindow *w)
|
||||||
appendChildNode(backgroundNode.get());
|
appendChildNode(backgroundNode.get());
|
||||||
|
|
||||||
for (auto &zNode: zNodes) {
|
for (auto &zNode: zNodes) {
|
||||||
zNode.reset(new QSGNode);
|
zNode.reset(new ZNode(true));
|
||||||
appendChildNode(zNode.get());
|
appendChildNode(zNode.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -268,8 +272,10 @@ void StatsView::plotAreaChanged(const QSizeF &s)
|
||||||
marker->updatePosition();
|
marker->updatePosition();
|
||||||
if (regressionItem)
|
if (regressionItem)
|
||||||
regressionItem->updatePosition();
|
regressionItem->updatePosition();
|
||||||
for (auto &marker: histogramMarkers)
|
if (meanMarker)
|
||||||
marker->updatePosition();
|
meanMarker->updatePosition();
|
||||||
|
if (medianMarker)
|
||||||
|
medianMarker->updatePosition();
|
||||||
if (legend)
|
if (legend)
|
||||||
legend->resize();
|
legend->resize();
|
||||||
updateTitlePos();
|
updateTitlePos();
|
||||||
|
@ -369,6 +375,8 @@ void StatsView::reset()
|
||||||
title.reset();
|
title.reset();
|
||||||
legend.reset();
|
legend.reset();
|
||||||
regressionItem.reset();
|
regressionItem.reset();
|
||||||
|
meanMarker.reset();
|
||||||
|
medianMarker.reset();
|
||||||
|
|
||||||
// Mark clean and dirty chart items for deletion
|
// Mark clean and dirty chart items for deletion
|
||||||
cleanItems.splice(deletedItems);
|
cleanItems.splice(deletedItems);
|
||||||
|
@ -376,7 +384,6 @@ void StatsView::reset()
|
||||||
|
|
||||||
series.clear();
|
series.clear();
|
||||||
quartileMarkers.clear();
|
quartileMarkers.clear();
|
||||||
histogramMarkers.clear();
|
|
||||||
grid.reset();
|
grid.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,10 +391,18 @@ void StatsView::plot(const StatsState &stateIn)
|
||||||
{
|
{
|
||||||
state = stateIn;
|
state = stateIn;
|
||||||
plotChart();
|
plotChart();
|
||||||
|
updateFeatures(); // Show / hide chart features, such as legend, etc.
|
||||||
plotAreaChanged(boundingRect().size());
|
plotAreaChanged(boundingRect().size());
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StatsView::updateFeatures(const StatsState &stateIn)
|
||||||
|
{
|
||||||
|
state = stateIn;
|
||||||
|
updateFeatures();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
void StatsView::plotChart()
|
void StatsView::plotChart()
|
||||||
{
|
{
|
||||||
if (!state.var1)
|
if (!state.var1)
|
||||||
|
@ -398,27 +413,26 @@ void StatsView::plotChart()
|
||||||
switch (state.type) {
|
switch (state.type) {
|
||||||
case ChartType::DiscreteBar:
|
case ChartType::DiscreteBar:
|
||||||
return plotBarChart(dives, state.subtype, state.var1, state.var1Binner, state.var2,
|
return plotBarChart(dives, state.subtype, state.var1, state.var1Binner, state.var2,
|
||||||
state.var2Binner, state.labels, state.legend);
|
state.var2Binner);
|
||||||
case ChartType::DiscreteValue:
|
case ChartType::DiscreteValue:
|
||||||
return plotValueChart(dives, state.subtype, state.var1, state.var1Binner, state.var2,
|
return plotValueChart(dives, state.subtype, state.var1, state.var1Binner, state.var2,
|
||||||
state.var2Operation, state.labels);
|
state.var2Operation);
|
||||||
case ChartType::DiscreteCount:
|
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:
|
case ChartType::Pie:
|
||||||
return plotPieChart(dives, state.var1, state.var1Binner, state.labels, state.legend);
|
return plotPieChart(dives, state.var1, state.var1Binner);
|
||||||
case ChartType::DiscreteBox:
|
case ChartType::DiscreteBox:
|
||||||
return plotDiscreteBoxChart(dives, state.var1, state.var1Binner, state.var2);
|
return plotDiscreteBoxChart(dives, state.var1, state.var1Binner, state.var2);
|
||||||
case ChartType::DiscreteScatter:
|
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:
|
case ChartType::HistogramCount:
|
||||||
return plotHistogramCountChart(dives, state.subtype, state.var1, state.var1Binner,
|
return plotHistogramCountChart(dives, state.subtype, state.var1, state.var1Binner);
|
||||||
state.labels, state.median, state.mean);
|
|
||||||
case ChartType::HistogramValue:
|
case ChartType::HistogramValue:
|
||||||
return plotHistogramValueChart(dives, state.subtype, state.var1, state.var1Binner, state.var2,
|
return plotHistogramValueChart(dives, state.subtype, state.var1, state.var1Binner, state.var2,
|
||||||
state.var2Operation, state.labels);
|
state.var2Operation);
|
||||||
case ChartType::HistogramStacked:
|
case ChartType::HistogramStacked:
|
||||||
return plotHistogramStackedChart(dives, state.subtype, state.var1, state.var1Binner,
|
return plotHistogramStackedChart(dives, state.subtype, state.var1, state.var1Binner,
|
||||||
state.var2, state.var2Binner, state.labels, state.legend);
|
state.var2, state.var2Binner);
|
||||||
case ChartType::HistogramBox:
|
case ChartType::HistogramBox:
|
||||||
return plotHistogramBoxChart(dives, state.var1, state.var1Binner, state.var2);
|
return plotHistogramBoxChart(dives, state.var1, state.var1Binner, state.var2);
|
||||||
case ChartType::ScatterPlot:
|
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>
|
template<typename T>
|
||||||
CategoryAxis *StatsView::createCategoryAxis(const QString &name, const StatsBinner &binner,
|
CategoryAxis *StatsView::createCategoryAxis(const QString &name, const StatsBinner &binner,
|
||||||
const std::vector<T> &bins, bool isHorizontal)
|
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
|
// From a list of counts, make (count, label) pairs, where the label
|
||||||
// formats the total number and the percentage of dives.
|
// 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,
|
static std::vector<std::pair<int, std::vector<QString>>> makeCountLabels(const std::vector<int> &counts, int total, bool isHorizontal)
|
||||||
bool labels, bool isHorizontal)
|
|
||||||
{
|
{
|
||||||
std::vector<std::pair<int, std::vector<QString>>> count_labels;
|
std::vector<std::pair<int, std::vector<QString>>> count_labels;
|
||||||
count_labels.reserve(counts.size());
|
count_labels.reserve(counts.size());
|
||||||
for (int count: counts) {
|
for (int count: counts) {
|
||||||
std::vector<QString> label = labels ? makePercentageLabels(count, total, isHorizontal)
|
std::vector<QString> label = makePercentageLabels(count, total, isHorizontal);
|
||||||
: std::vector<QString>();
|
|
||||||
count_labels.push_back(std::make_pair(count, label));
|
count_labels.push_back(std::make_pair(count, label));
|
||||||
}
|
}
|
||||||
return count_labels;
|
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,
|
void StatsView::plotBarChart(const std::vector<dive *> &dives,
|
||||||
ChartSubType subType,
|
ChartSubType subType,
|
||||||
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
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)
|
if (!categoryBinner || !valueBinner)
|
||||||
return;
|
return;
|
||||||
|
@ -561,14 +592,13 @@ void StatsView::plotBarChart(const std::vector<dive *> &dives,
|
||||||
setAxes(catAxis, valAxis);
|
setAxes(catAxis, valAxis);
|
||||||
|
|
||||||
// Paint legend first, because the bin-names will be moved away from.
|
// 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;
|
std::vector<BarSeries::MultiItem> items;
|
||||||
items.reserve(data.hbin_counts.size());
|
items.reserve(data.hbin_counts.size());
|
||||||
double pos = 0.0;
|
double pos = 0.0;
|
||||||
for (auto &[hbin, counts, total]: data.hbin_counts) {
|
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) });
|
categoryBinner->formatWithUnit(*hbin) });
|
||||||
pos += 1.0;
|
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,
|
void StatsView::plotValueChart(const std::vector<dive *> &dives,
|
||||||
ChartSubType subType,
|
ChartSubType subType,
|
||||||
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
||||||
const StatsVariable *valueVariable, StatsOperation valueAxisOperation,
|
const StatsVariable *valueVariable, StatsOperation valueAxisOperation)
|
||||||
bool labels)
|
|
||||||
{
|
{
|
||||||
if (!categoryBinner)
|
if (!categoryBinner)
|
||||||
return;
|
return;
|
||||||
|
@ -681,8 +710,7 @@ void StatsView::plotValueChart(const std::vector<dive *> &dives,
|
||||||
if (res.isValid()) {
|
if (res.isValid()) {
|
||||||
double height = res.get(valueAxisOperation);
|
double height = res.get(valueAxisOperation);
|
||||||
QString value = QString("%L1").arg(height, 0, 'f', decimals);
|
QString value = QString("%L1").arg(height, 0, 'f', decimals);
|
||||||
std::vector<QString> label = labels ? std::vector<QString> { value }
|
std::vector<QString> label = std::vector<QString> { value };
|
||||||
: std::vector<QString>();
|
|
||||||
items.push_back({ pos - 0.5, pos + 0.5, height, label,
|
items.push_back({ pos - 0.5, pos + 0.5, height, label,
|
||||||
categoryBinner->formatWithUnit(*bin), res });
|
categoryBinner->formatWithUnit(*bin), res });
|
||||||
}
|
}
|
||||||
|
@ -713,8 +741,7 @@ static int getMaxCount(const std::vector<T> &bins)
|
||||||
|
|
||||||
void StatsView::plotDiscreteCountChart(const std::vector<dive *> &dives,
|
void StatsView::plotDiscreteCountChart(const std::vector<dive *> &dives,
|
||||||
ChartSubType subType,
|
ChartSubType subType,
|
||||||
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner)
|
||||||
bool labels)
|
|
||||||
{
|
{
|
||||||
if (!categoryBinner)
|
if (!categoryBinner)
|
||||||
return;
|
return;
|
||||||
|
@ -745,8 +772,7 @@ void StatsView::plotDiscreteCountChart(const std::vector<dive *> &dives,
|
||||||
items.reserve(categoryBins.size());
|
items.reserve(categoryBins.size());
|
||||||
double pos = 0.0;
|
double pos = 0.0;
|
||||||
for (auto const &[bin, count]: categoryBins) {
|
for (auto const &[bin, count]: categoryBins) {
|
||||||
std::vector<QString> label = labels ? makePercentageLabels(count, total, isHorizontal)
|
std::vector<QString> label = makePercentageLabels(count, total, isHorizontal);
|
||||||
: std::vector<QString>();
|
|
||||||
items.push_back({ pos - 0.5, pos + 0.5, count, label,
|
items.push_back({ pos - 0.5, pos + 0.5, count, label,
|
||||||
categoryBinner->formatWithUnit(*bin), total });
|
categoryBinner->formatWithUnit(*bin), total });
|
||||||
pos += 1.0;
|
pos += 1.0;
|
||||||
|
@ -756,8 +782,7 @@ void StatsView::plotDiscreteCountChart(const std::vector<dive *> &dives,
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatsView::plotPieChart(const std::vector<dive *> &dives,
|
void StatsView::plotPieChart(const std::vector<dive *> &dives,
|
||||||
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner)
|
||||||
bool labels, bool showLegend)
|
|
||||||
{
|
{
|
||||||
if (!categoryBinner)
|
if (!categoryBinner)
|
||||||
return;
|
return;
|
||||||
|
@ -776,10 +801,9 @@ void StatsView::plotPieChart(const std::vector<dive *> &dives,
|
||||||
data.emplace_back(categoryBinner->formatWithUnit(*bin), count);
|
data.emplace_back(categoryBinner->formatWithUnit(*bin), count);
|
||||||
|
|
||||||
bool keepOrder = categoryVariable->type() != StatsVariable::Type::Discrete;
|
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,
|
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,
|
void StatsView::plotDiscreteScatter(const std::vector<dive *> &dives,
|
||||||
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
||||||
const StatsVariable *valueVariable, bool quartiles)
|
const StatsVariable *valueVariable)
|
||||||
{
|
{
|
||||||
if (!categoryBinner)
|
if (!categoryBinner)
|
||||||
return;
|
return;
|
||||||
|
@ -846,26 +870,19 @@ void StatsView::plotDiscreteScatter(const std::vector<dive *> &dives,
|
||||||
for (const auto &[bin, array]: categoryBins) {
|
for (const auto &[bin, array]: categoryBins) {
|
||||||
for (auto [v, d]: array)
|
for (auto [v, d]: array)
|
||||||
series->append(d, x, v);
|
series->append(d, x, v);
|
||||||
if (quartiles) {
|
StatsQuartiles quartiles = StatsVariable::quartiles(array);
|
||||||
StatsQuartiles quartiles = StatsVariable::quartiles(array);
|
if (quartiles.isValid()) {
|
||||||
if (quartiles.isValid()) {
|
quartileMarkers.push_back(createChartItem<QuartileMarker>(
|
||||||
quartileMarkers.push_back(createChartItem<QuartileMarker>(
|
x, quartiles.q1, catAxis, valAxis));
|
||||||
x, quartiles.q1, catAxis, valAxis));
|
quartileMarkers.push_back(createChartItem<QuartileMarker>(
|
||||||
quartileMarkers.push_back(createChartItem<QuartileMarker>(
|
x, quartiles.q2, catAxis, valAxis));
|
||||||
x, quartiles.q2, catAxis, valAxis));
|
quartileMarkers.push_back(createChartItem<QuartileMarker>(
|
||||||
quartileMarkers.push_back(createChartItem<QuartileMarker>(
|
x, quartiles.q3, catAxis, valAxis));
|
||||||
x, quartiles.q3, catAxis, valAxis));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
x += 1.0;
|
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.
|
// 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.
|
// To create a category axis from this, we have to templatify the function.
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -890,8 +907,7 @@ HistogramAxis *StatsView::createHistogramAxis(const QString &name, const StatsBi
|
||||||
|
|
||||||
void StatsView::plotHistogramCountChart(const std::vector<dive *> &dives,
|
void StatsView::plotHistogramCountChart(const std::vector<dive *> &dives,
|
||||||
ChartSubType subType,
|
ChartSubType subType,
|
||||||
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner)
|
||||||
bool labels, bool showMedian, bool showMean)
|
|
||||||
{
|
{
|
||||||
if (!categoryBinner)
|
if (!categoryBinner)
|
||||||
return;
|
return;
|
||||||
|
@ -924,8 +940,7 @@ void StatsView::plotHistogramCountChart(const std::vector<dive *> &dives,
|
||||||
for (auto const &[bin, count]: categoryBins) {
|
for (auto const &[bin, count]: categoryBins) {
|
||||||
double lowerBound = categoryBinner->lowerBoundToFloat(*bin);
|
double lowerBound = categoryBinner->lowerBoundToFloat(*bin);
|
||||||
double upperBound = categoryBinner->upperBoundToFloat(*bin);
|
double upperBound = categoryBinner->upperBoundToFloat(*bin);
|
||||||
std::vector<QString> label = labels ? makePercentageLabels(count, total, isHorizontal)
|
std::vector<QString> label = makePercentageLabels(count, total, isHorizontal);
|
||||||
: std::vector<QString>();
|
|
||||||
|
|
||||||
items.push_back({ lowerBound, upperBound, count, label,
|
items.push_back({ lowerBound, upperBound, count, label,
|
||||||
categoryBinner->formatWithUnit(*bin), total });
|
categoryBinner->formatWithUnit(*bin), total });
|
||||||
|
@ -934,24 +949,19 @@ void StatsView::plotHistogramCountChart(const std::vector<dive *> &dives,
|
||||||
createSeries<BarSeries>(isHorizontal, categoryVariable->name(), items);
|
createSeries<BarSeries>(isHorizontal, categoryVariable->name(), items);
|
||||||
|
|
||||||
if (categoryVariable->type() == StatsVariable::Type::Numeric) {
|
if (categoryVariable->type() == StatsVariable::Type::Numeric) {
|
||||||
if (showMean) {
|
double mean = categoryVariable->mean(dives);
|
||||||
double mean = categoryVariable->mean(dives);
|
if (!std::isnan(mean))
|
||||||
if (!std::isnan(mean))
|
meanMarker = createChartItem<HistogramMarker>(mean, isHorizontal, Qt::green, xAxis, yAxis);
|
||||||
addHistogramMarker(mean, Qt::green, isHorizontal, xAxis, yAxis);
|
double median = categoryVariable->quartiles(dives).q2;
|
||||||
}
|
if (!std::isnan(median))
|
||||||
if (showMedian) {
|
medianMarker = createChartItem<HistogramMarker>(median, isHorizontal, Qt::red, xAxis, yAxis);
|
||||||
double median = categoryVariable->quartiles(dives).q2;
|
|
||||||
if (!std::isnan(median))
|
|
||||||
addHistogramMarker(median, Qt::red, isHorizontal, xAxis, yAxis);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatsView::plotHistogramValueChart(const std::vector<dive *> &dives,
|
void StatsView::plotHistogramValueChart(const std::vector<dive *> &dives,
|
||||||
ChartSubType subType,
|
ChartSubType subType,
|
||||||
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
||||||
const StatsVariable *valueVariable, StatsOperation valueAxisOperation,
|
const StatsVariable *valueVariable, StatsOperation valueAxisOperation)
|
||||||
bool labels)
|
|
||||||
{
|
{
|
||||||
if (!categoryBinner)
|
if (!categoryBinner)
|
||||||
return;
|
return;
|
||||||
|
@ -990,8 +1000,7 @@ void StatsView::plotHistogramValueChart(const std::vector<dive *> &dives,
|
||||||
double lowerBound = categoryBinner->lowerBoundToFloat(*bin);
|
double lowerBound = categoryBinner->lowerBoundToFloat(*bin);
|
||||||
double upperBound = categoryBinner->upperBoundToFloat(*bin);
|
double upperBound = categoryBinner->upperBoundToFloat(*bin);
|
||||||
QString value = QString("%L1").arg(height, 0, 'f', decimals);
|
QString value = QString("%L1").arg(height, 0, 'f', decimals);
|
||||||
std::vector<QString> label = labels ? std::vector<QString> { value }
|
std::vector<QString> label = std::vector<QString> { value };
|
||||||
: std::vector<QString>();
|
|
||||||
items.push_back({ lowerBound, upperBound, height, label,
|
items.push_back({ lowerBound, upperBound, height, label,
|
||||||
categoryBinner->formatWithUnit(*bin), res });
|
categoryBinner->formatWithUnit(*bin), res });
|
||||||
}
|
}
|
||||||
|
@ -1002,7 +1011,7 @@ void StatsView::plotHistogramValueChart(const std::vector<dive *> &dives,
|
||||||
void StatsView::plotHistogramStackedChart(const std::vector<dive *> &dives,
|
void StatsView::plotHistogramStackedChart(const std::vector<dive *> &dives,
|
||||||
ChartSubType subType,
|
ChartSubType subType,
|
||||||
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
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)
|
if (!categoryBinner || !valueBinner)
|
||||||
return;
|
return;
|
||||||
|
@ -1018,8 +1027,7 @@ void StatsView::plotHistogramStackedChart(const std::vector<dive *> &dives,
|
||||||
*categoryBinner, categoryBins, !isHorizontal);
|
*categoryBinner, categoryBins, !isHorizontal);
|
||||||
|
|
||||||
BarPlotData data(categoryBins, *valueBinner);
|
BarPlotData data(categoryBins, *valueBinner);
|
||||||
if (showLegend)
|
legend = createChartItem<Legend>(data.vbinNames);
|
||||||
legend = createChartItem<Legend>(data.vbinNames);
|
|
||||||
|
|
||||||
CountAxis *valAxis = createCountAxis(data.maxCategoryCount, isHorizontal);
|
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) {
|
for (auto &[hbin, counts, total]: data.hbin_counts) {
|
||||||
double lowerBound = categoryBinner->lowerBoundToFloat(*hbin);
|
double lowerBound = categoryBinner->lowerBoundToFloat(*hbin);
|
||||||
double upperBound = categoryBinner->upperBoundToFloat(*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) });
|
categoryBinner->formatWithUnit(*hbin) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,8 @@ public:
|
||||||
~StatsView();
|
~StatsView();
|
||||||
|
|
||||||
void plot(const StatsState &state);
|
void plot(const StatsState &state);
|
||||||
QQuickWindow *w() const; // Make window available to items
|
void updateFeatures(const StatsState &state); // Updates the visibility of chart features, such as legend, regression, etc.
|
||||||
|
QQuickWindow *w() const; // Make window available to items
|
||||||
QSizeF size() const;
|
QSizeF size() const;
|
||||||
QRectF plotArea() const;
|
QRectF plotArea() const;
|
||||||
void addQSGNode(QSGNode *node, ChartZValue z); // Must only be called in render thread!
|
void addQSGNode(QSGNode *node, ChartZValue z); // Must only be called in render thread!
|
||||||
|
@ -75,39 +76,39 @@ private:
|
||||||
void plotBarChart(const std::vector<dive *> &dives,
|
void plotBarChart(const std::vector<dive *> &dives,
|
||||||
ChartSubType subType,
|
ChartSubType subType,
|
||||||
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
||||||
const StatsVariable *valueVariable, const StatsBinner *valueBinner, bool labels, bool legend);
|
const StatsVariable *valueVariable, const StatsBinner *valueBinner);
|
||||||
void plotValueChart(const std::vector<dive *> &dives,
|
void plotValueChart(const std::vector<dive *> &dives,
|
||||||
ChartSubType subType,
|
ChartSubType subType,
|
||||||
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
||||||
const StatsVariable *valueVariable, StatsOperation valueAxisOperation, bool labels);
|
const StatsVariable *valueVariable, StatsOperation valueAxisOperation);
|
||||||
void plotDiscreteCountChart(const std::vector<dive *> &dives,
|
void plotDiscreteCountChart(const std::vector<dive *> &dives,
|
||||||
ChartSubType subType,
|
ChartSubType subType,
|
||||||
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner, bool labels);
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner);
|
||||||
void plotPieChart(const std::vector<dive *> &dives,
|
void plotPieChart(const std::vector<dive *> &dives,
|
||||||
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner, bool labels, bool legend);
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner);
|
||||||
void plotDiscreteBoxChart(const std::vector<dive *> &dives,
|
void plotDiscreteBoxChart(const std::vector<dive *> &dives,
|
||||||
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner, const StatsVariable *valueVariable);
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner, const StatsVariable *valueVariable);
|
||||||
void plotDiscreteScatter(const std::vector<dive *> &dives,
|
void plotDiscreteScatter(const std::vector<dive *> &dives,
|
||||||
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
||||||
const StatsVariable *valueVariable, bool quartiles);
|
const StatsVariable *valueVariable);
|
||||||
void plotHistogramCountChart(const std::vector<dive *> &dives,
|
void plotHistogramCountChart(const std::vector<dive *> &dives,
|
||||||
ChartSubType subType,
|
ChartSubType subType,
|
||||||
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner);
|
||||||
bool labels, bool showMedian, bool showMean);
|
|
||||||
void plotHistogramValueChart(const std::vector<dive *> &dives,
|
void plotHistogramValueChart(const std::vector<dive *> &dives,
|
||||||
ChartSubType subType,
|
ChartSubType subType,
|
||||||
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
||||||
const StatsVariable *valueVariable, StatsOperation valueAxisOperation, bool labels);
|
const StatsVariable *valueVariable, StatsOperation valueAxisOperation);
|
||||||
void plotHistogramStackedChart(const std::vector<dive *> &dives,
|
void plotHistogramStackedChart(const std::vector<dive *> &dives,
|
||||||
ChartSubType subType,
|
ChartSubType subType,
|
||||||
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
||||||
const StatsVariable *valueVariable, const StatsBinner *valueBinner, bool labels, bool legend);
|
const StatsVariable *valueVariable, const StatsBinner *valueBinner);
|
||||||
void plotHistogramBoxChart(const std::vector<dive *> &dives,
|
void plotHistogramBoxChart(const std::vector<dive *> &dives,
|
||||||
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner, const StatsVariable *valueVariable);
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner, const StatsVariable *valueVariable);
|
||||||
void plotScatter(const std::vector<dive *> &dives, const StatsVariable *categoryVariable, const StatsVariable *valueVariable);
|
void plotScatter(const std::vector<dive *> &dives, const StatsVariable *categoryVariable, const StatsVariable *valueVariable);
|
||||||
void setTitle(const QString &);
|
void setTitle(const QString &);
|
||||||
void updateTitlePos(); // After resizing, set title to correct position
|
void updateTitlePos(); // After resizing, set title to correct position
|
||||||
void plotChart();
|
void plotChart();
|
||||||
|
void updateFeatures(); // Updates the visibility of chart features, such as legend, regression, etc.
|
||||||
|
|
||||||
template <typename T, class... Args>
|
template <typename T, class... Args>
|
||||||
T *createSeries(Args&&... args);
|
T *createSeries(Args&&... args);
|
||||||
|
@ -126,14 +127,13 @@ private:
|
||||||
// Helper functions to add feature to the chart
|
// Helper functions to add feature to the chart
|
||||||
void addLineMarker(double pos, double low, double high, const QPen &pen, bool isHorizontal);
|
void addLineMarker(double pos, double low, double high, const QPen &pen, bool isHorizontal);
|
||||||
|
|
||||||
void addHistogramMarker(double pos, QColor color, bool isHorizontal, StatsAxis *xAxis, StatsAxis *yAxis);
|
|
||||||
|
|
||||||
StatsState state;
|
StatsState state;
|
||||||
QFont titleFont;
|
QFont titleFont;
|
||||||
std::vector<std::unique_ptr<StatsSeries>> series;
|
std::vector<std::unique_ptr<StatsSeries>> series;
|
||||||
std::unique_ptr<StatsGrid> grid;
|
std::unique_ptr<StatsGrid> grid;
|
||||||
std::vector<ChartItemPtr<QuartileMarker>> quartileMarkers;
|
std::vector<ChartItemPtr<QuartileMarker>> quartileMarkers;
|
||||||
std::vector<ChartItemPtr<HistogramMarker>> histogramMarkers;
|
ChartItemPtr<HistogramMarker> medianMarker, meanMarker;
|
||||||
StatsSeries *highlightedSeries;
|
StatsSeries *highlightedSeries;
|
||||||
StatsAxis *xAxis, *yAxis;
|
StatsAxis *xAxis, *yAxis;
|
||||||
ChartItemPtr<ChartTextItem> title;
|
ChartItemPtr<ChartTextItem> title;
|
||||||
|
|
Loading…
Add table
Reference in a new issue