aboutsummaryrefslogtreecommitdiffstats
path: root/stats
diff options
context:
space:
mode:
authorGravatar Berthold Stoeger <bstoeger@mail.tuwien.ac.at>2021-01-13 16:19:27 +0100
committerGravatar bstoeger <32835590+bstoeger@users.noreply.github.com>2021-01-20 08:47:18 +0100
commite1c0cace95d6ed19dff37a524c7f4b2288d258d7 (patch)
tree403215ac791cf0ae6eda52007c1a5feedd0b844d /stats
parent785d5189f617bab7d5dde668563fc4889e46f695 (diff)
downloadsubsurface-e1c0cace95d6ed19dff37a524c7f4b2288d258d7.tar.gz
statistics: add notion of Z-value to chart items
The chart items were drawn in order of creation. To control this, add a notion of Z-value. In contrast to QGraphicsScene, make this a small integer value. To controll order of drawing, a plain QSGNode is created for every possible Z-Value and items are added to these nodes. Thus, items are rendered by Z-value and if the Z-value is equal by order of creation. Likewise split the list of chart-items into Z-values, so that items can be quickly unregistered: The items that will be removed individually will usuall be part of Z-levels with only few items (e.g. legend, infobox). Z-levels with many items (notably the series) will always be fully rebuilt. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Diffstat (limited to 'stats')
-rw-r--r--stats/chartitem.cpp9
-rw-r--r--stats/chartitem.h6
-rw-r--r--stats/informationbox.cpp3
-rw-r--r--stats/legend.cpp3
-rw-r--r--stats/statsview.cpp70
-rw-r--r--stats/statsview.h11
-rw-r--r--stats/zvalues.h11
7 files changed, 82 insertions, 31 deletions
diff --git a/stats/chartitem.cpp b/stats/chartitem.cpp
index c0fc50841..68e7fa371 100644
--- a/stats/chartitem.cpp
+++ b/stats/chartitem.cpp
@@ -12,8 +12,8 @@ static int round_up(double f)
return static_cast<int>(ceil(f));
}
-ChartItem::ChartItem(StatsView &v) :
- dirty(false), view(v), positionDirty(false), textureDirty(false)
+ChartItem::ChartItem(StatsView &v, ChartZValue z) :
+ dirty(false), zValue(z), view(v), positionDirty(false), textureDirty(false)
{
}
@@ -46,7 +46,7 @@ void ChartItem::render()
return;
if (!node) {
node.reset(view.w()->createImageNode());
- view.addQSGNode(node.get(), 0);
+ view.addQSGNode(node.get(), zValue);
}
if (!img) {
resize(QSizeF(1,1));
@@ -85,7 +85,8 @@ QRectF ChartItem::getRect() const
return rect;
}
-ChartRectItem::ChartRectItem(StatsView &v, const QPen &pen, const QBrush &brush, double radius) : ChartItem(v),
+ChartRectItem::ChartRectItem(StatsView &v, ChartZValue z,
+ const QPen &pen, const QBrush &brush, double radius) : ChartItem(v, z),
pen(pen), brush(brush), radius(radius)
{
}
diff --git a/stats/chartitem.h b/stats/chartitem.h
index fb4b67dff..8cb9ef3a7 100644
--- a/stats/chartitem.h
+++ b/stats/chartitem.h
@@ -10,10 +10,11 @@
class QSGImageNode;
class QSGTexture;
class StatsView;
+enum class ChartZValue : int;
class ChartItem {
public:
- ChartItem(StatsView &v);
+ ChartItem(StatsView &v, ChartZValue z);
~ChartItem();
// Attention: The children are responsible for updating the item. None of these calls will.
void resize(QSizeF size); // Resets the canvas. Attention: image is *unitialized*.
@@ -21,6 +22,7 @@ public:
void render(); // Only call on render thread!
QRectF getRect() const;
bool dirty; // If true, call render() when rebuilding the scene
+ const ChartZValue zValue;
protected:
std::unique_ptr<QPainter> painter;
std::unique_ptr<QImage> img;
@@ -39,7 +41,7 @@ private:
// Draw a rectangular background after resize. Children are responsible for calling update().
class ChartRectItem : public ChartItem {
public:
- ChartRectItem(StatsView &v, const QPen &pen, const QBrush &brush, double radius);
+ ChartRectItem(StatsView &v, ChartZValue z, const QPen &pen, const QBrush &brush, double radius);
~ChartRectItem();
void resize(QSizeF size);
private:
diff --git a/stats/informationbox.cpp b/stats/informationbox.cpp
index c768b77b3..02fdb9e97 100644
--- a/stats/informationbox.cpp
+++ b/stats/informationbox.cpp
@@ -12,7 +12,8 @@ static const double informationBorderRadius = 4.0; // Radius of rounded corners
static const int distanceFromPointer = 10; // Distance to place box from mouse pointer or scatter item
InformationBox::InformationBox(StatsView &v) :
- ChartRectItem(v, QPen(informationBorderColor, informationBorder),
+ ChartRectItem(v, ChartZValue::InformationBox,
+ QPen(informationBorderColor, informationBorder),
QBrush(informationColor), informationBorderRadius)
{
}
diff --git a/stats/legend.cpp b/stats/legend.cpp
index 2cbd883db..590de21b0 100644
--- a/stats/legend.cpp
+++ b/stats/legend.cpp
@@ -15,7 +15,8 @@ static const QColor legendColor(0x00, 0x8e, 0xcc, 192); // Note: fourth argument
static const QColor legendBorderColor(Qt::black);
Legend::Legend(StatsView &view, const std::vector<QString> &names) :
- ChartRectItem(view, QPen(legendBorderColor, legendBorderSize), QBrush(legendColor), legendBoxBorderRadius),
+ ChartRectItem(view, ChartZValue::Legend,
+ QPen(legendBorderColor, legendBorderSize), QBrush(legendColor), legendBoxBorderRadius),
displayedItems(0), width(0.0), height(0.0),
font(QFont()) // Make configurable
{
diff --git a/stats/statsview.cpp b/stats/statsview.cpp
index 05693fd0b..983b128fe 100644
--- a/stats/statsview.cpp
+++ b/stats/statsview.cpp
@@ -80,17 +80,33 @@ void StatsView::mouseReleaseEvent(QMouseEvent *)
}
}
+class RootNode : public QSGNode
+{
+public:
+ RootNode();
+ QSGImageNode *imageNode; // imageNode to plot QGRaphicsScene on. Remove in due course.
+ // We entertain one node per Z-level.
+ std::array<QSGNode *, (size_t)ChartZValue::Count> zNodes;
+ std::array<std::vector<ChartItem *>, (size_t)ChartZValue::Count> items;
+};
+
+RootNode::RootNode()
+{
+ for (QSGNode *&zNode: zNodes) {
+ zNode = new QSGNode;
+ appendChildNode(zNode);
+ }
+}
+
QSGNode *StatsView::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
{
// The QtQuick drawing interface is utterly bizzare with a distinct 1980ies-style memory management.
// This is just a copy of what is found in Qt's documentation.
- QSGImageNode *n = static_cast<QSGImageNode *>(oldNode);
- if (!n)
- n = rootNode = window()->createImageNode();
-
- for (ChartItem *item: items) {
- if (item->dirty)
- item->render();
+ RootNode *n = static_cast<RootNode *>(oldNode);
+ if (!n) {
+ n = rootNode = new RootNode;
+ n->imageNode = window()->createImageNode();
+ n->zNodes[(int)ChartZValue::Series]->appendChildNode(n->imageNode);
}
QRectF rect = boundingRect();
@@ -99,28 +115,43 @@ QSGNode *StatsView::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNod
plotAreaChanged(plotRect.size());
}
+ for (auto &v: n->items) {
+ for (ChartItem *item: v) {
+ if (item->dirty)
+ item->render();
+ }
+ }
+
img->fill(backgroundColor);
scene.render(painter.get());
texture.reset(window()->createTextureFromImage(*img, QQuickWindow::TextureIsOpaque));
- n->setTexture(texture.get());
- n->setRect(rect);
+ n->imageNode->setTexture(texture.get());
+ n->imageNode->setRect(rect);
return n;
}
-void StatsView::addQSGNode(QSGNode *node, int)
+void StatsView::addQSGNode(QSGNode *node, ChartZValue z)
{
- rootNode->appendChildNode(node);
+ int idx = std::clamp((int)z, 0, (int)ChartZValue::Count - 1);
+ rootNode->zNodes[idx]->appendChildNode(node);
}
// Currently this does an inefficient linear search in the chart-item vector.
-// The reason is that removing individual chart items is very rare: for now,
-// it is only done when hiding an InfoBox. In the future, this might have to
-// be improved.
+// However, we entertain one vector of items per Z-value and currently
+// only the infobox is explicitly deleted, which has a unique Z-value.
void StatsView::unregisterChartItem(const ChartItem *item)
{
- auto it = std::find(items.begin(), items.end(), item);
- if (it != items.end())
- items.erase(it);
+ int idx = std::clamp((int)item->zValue, 0, (int)ChartZValue::Count - 1);
+ std::vector<ChartItem *> &v = rootNode->items[idx];
+ auto it = std::find(v.begin(), v.end(), item);
+ if (it != v.end())
+ v.erase(it);
+}
+
+void StatsView::registerChartItem(ChartItem *item)
+{
+ int idx = std::clamp((int)item->zValue, 0, (int)ChartZValue::Count - 1);
+ rootNode->items[idx].push_back(item);
}
QQuickWindow *StatsView::w() const
@@ -292,7 +323,10 @@ void StatsView::reset()
highlightedSeries = nullptr;
xAxis = yAxis = nullptr;
draggedItem = nullptr;
- items.clear(); // non-owning pointers
+ if (rootNode) {
+ for (auto &v: rootNode->items)
+ v.clear(); // non-owning pointers
+ }
legend.reset();
series.clear();
quartileMarkers.clear();
diff --git a/stats/statsview.h b/stats/statsview.h
index 62fb246cd..1a922ac98 100644
--- a/stats/statsview.h
+++ b/stats/statsview.h
@@ -19,7 +19,6 @@ struct StatsVariable;
class QGraphicsLineItem;
class QGraphicsSimpleTextItem;
-class QSGImageNode;
class StatsSeries;
class CategoryAxis;
class ChartItem;
@@ -29,8 +28,10 @@ class StatsAxis;
class StatsGrid;
class Legend;
class QSGTexture;
+class RootNode; // Internal implementation detail
enum class ChartSubType : int;
+enum class ChartZValue : int;
enum class StatsOperation : int;
struct regression_data {
@@ -50,7 +51,8 @@ public:
void plot(const StatsState &state);
QQuickWindow *w() const; // Make window available to items
QSizeF size() const;
- void addQSGNode(QSGNode *node, int z); // Must only be called in render thread!
+ void addQSGNode(QSGNode *node, ChartZValue z); // Must only be called in render thread!
+ void registerChartItem(ChartItem *item);
void unregisterChartItem(const ChartItem *item);
template <typename T, class... Args>
std::unique_ptr<T> createChartItem(Args&&... args);
@@ -165,7 +167,6 @@ private:
std::vector<RegressionLine> regressionLines;
std::vector<HistogramMarker> histogramMarkers;
std::unique_ptr<QGraphicsSimpleTextItem> title;
- std::vector<ChartItem *> items; // Attention: currently, items are not automatically removed on destruction!
StatsSeries *highlightedSeries;
StatsAxis *xAxis, *yAxis;
Legend *draggedItem;
@@ -176,7 +177,7 @@ private:
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
- QSGImageNode *rootNode;
+ RootNode *rootNode;
};
// This implementation detail must be known to users of the class.
@@ -185,7 +186,7 @@ template <typename T, class... Args>
std::unique_ptr<T> StatsView::createChartItem(Args&&... args)
{
std::unique_ptr<T> res(new T(*this, std::forward<Args>(args)...));
- items.push_back(res.get());
+ registerChartItem(res.get());
return res;
}
diff --git a/stats/zvalues.h b/stats/zvalues.h
index 118c488c0..d6e9c7d60 100644
--- a/stats/zvalues.h
+++ b/stats/zvalues.h
@@ -15,4 +15,15 @@ struct ZValues {
static constexpr double legend = 5.0;
};
+enum class ChartZValue {
+ Grid = 0,
+ Series,
+ Axes,
+ SeriesLabels,
+ ChartFeatures, // quartile markers and regression lines
+ InformationBox,
+ Legend,
+ Count
+};
+
#endif