aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Subsurface-mobile.pro2
-rw-r--r--stats/CMakeLists.txt2
-rw-r--r--stats/chartitem.cpp77
-rw-r--r--stats/chartitem.h50
-rw-r--r--stats/quartilemarker.cpp30
-rw-r--r--stats/quartilemarker.h20
-rw-r--r--stats/statsview.cpp37
-rw-r--r--stats/statsview.h12
8 files changed, 172 insertions, 58 deletions
diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro
index e2a724633..b113eebfe 100644
--- a/Subsurface-mobile.pro
+++ b/Subsurface-mobile.pro
@@ -136,6 +136,7 @@ SOURCES += subsurface-mobile-main.cpp \
stats/informationbox.cpp \
stats/legend.cpp \
stats/pieseries.cpp \
+ stats/quartilemarker.cpp \
stats/scatterseries.cpp \
stats/statsaxis.cpp \
stats/statscolors.cpp \
@@ -285,6 +286,7 @@ HEADERS += \
stats/informationbox.h \
stats/legend.h \
stats/pieseries.h \
+ stats/quartilemarker.h \
stats/scatterseries.h \
stats/statsaxis.h \
stats/statscolors.h \
diff --git a/stats/CMakeLists.txt b/stats/CMakeLists.txt
index c0fbe3c51..105096436 100644
--- a/stats/CMakeLists.txt
+++ b/stats/CMakeLists.txt
@@ -19,6 +19,8 @@ set(SUBSURFACE_STATS_SRCS
legend.cpp
pieseries.h
pieseries.cpp
+ quartilemarker.h
+ quartilemarker.cpp
scatterseries.h
scatterseries.cpp
statsaxis.h
diff --git a/stats/chartitem.cpp b/stats/chartitem.cpp
index 68e7fa371..6fef5e44f 100644
--- a/stats/chartitem.cpp
+++ b/stats/chartitem.cpp
@@ -4,6 +4,7 @@
#include <cmath>
#include <QQuickWindow>
+#include <QSGFlatColorMaterial>
#include <QSGImageNode>
#include <QSGTexture>
@@ -13,13 +14,12 @@ static int round_up(double f)
}
ChartItem::ChartItem(StatsView &v, ChartZValue z) :
- dirty(false), zValue(z), view(v), positionDirty(false), textureDirty(false)
+ dirty(false), zValue(z), view(v)
{
}
ChartItem::~ChartItem()
{
- painter.reset(); // Make sure to destroy painter before image that is painted on
view.unregisterChartItem(this);
}
@@ -28,19 +28,29 @@ QSizeF ChartItem::sceneSize() const
return view.size();
}
-void ChartItem::setTextureDirty()
+ChartPixmapItem::ChartPixmapItem(StatsView &v, ChartZValue z) : ChartItem(v, z),
+ positionDirty(false), textureDirty(false)
+{
+}
+
+ChartPixmapItem::~ChartPixmapItem()
+{
+ painter.reset(); // Make sure to destroy painter before image that is painted on
+}
+
+void ChartPixmapItem::setTextureDirty()
{
textureDirty = true;
dirty = true;
}
-void ChartItem::setPositionDirty()
+void ChartPixmapItem::setPositionDirty()
{
positionDirty = true;
dirty = true;
}
-void ChartItem::render()
+void ChartPixmapItem::render()
{
if (!dirty)
return;
@@ -64,7 +74,7 @@ void ChartItem::render()
dirty = false;
}
-void ChartItem::resize(QSizeF size)
+void ChartPixmapItem::resize(QSizeF size)
{
painter.reset();
img.reset(new QImage(round_up(size.width()), round_up(size.height()), QImage::Format_ARGB32));
@@ -74,19 +84,19 @@ void ChartItem::resize(QSizeF size)
setTextureDirty();
}
-void ChartItem::setPos(QPointF pos)
+void ChartPixmapItem::setPos(QPointF pos)
{
rect.moveTopLeft(pos);
setPositionDirty();
}
-QRectF ChartItem::getRect() const
+QRectF ChartPixmapItem::getRect() const
{
return rect;
}
ChartRectItem::ChartRectItem(StatsView &v, ChartZValue z,
- const QPen &pen, const QBrush &brush, double radius) : ChartItem(v, z),
+ const QPen &pen, const QBrush &brush, double radius) : ChartPixmapItem(v, z),
pen(pen), brush(brush), radius(radius)
{
}
@@ -97,7 +107,7 @@ ChartRectItem::~ChartRectItem()
void ChartRectItem::resize(QSizeF size)
{
- ChartItem::resize(size);
+ ChartPixmapItem::resize(size);
img->fill(Qt::transparent);
painter->setPen(pen);
painter->setBrush(brush);
@@ -106,3 +116,50 @@ void ChartRectItem::resize(QSizeF size)
QRect rect(width / 2, width / 2, imgSize.width() - width, imgSize.height() - width);
painter->drawRoundedRect(rect, radius, radius, Qt::AbsoluteSize);
}
+
+ChartLineItem::ChartLineItem(StatsView &v, ChartZValue z, QColor color, double width) : ChartItem(v, z),
+ color(color), width(width), positionDirty(false), materialDirty(false)
+{
+}
+
+ChartLineItem::~ChartLineItem()
+{
+}
+
+void ChartLineItem::render()
+{
+ if (!node) {
+ geometry.reset(new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 2));
+ geometry->setDrawingMode(QSGGeometry::DrawLines);
+ material.reset(new QSGFlatColorMaterial);
+ node.reset(new QSGGeometryNode);
+ node->setGeometry(geometry.get());
+ node->setMaterial(material.get());
+ view.addQSGNode(node.get(), zValue);
+ positionDirty = materialDirty = true;
+ }
+
+ if (positionDirty) {
+ // Attention: width is a geometry property and therefore handled by position dirty!
+ geometry->setLineWidth(static_cast<float>(width));
+ auto vertices = geometry->vertexDataAsPoint2D();
+ vertices[0].set(static_cast<float>(from.x()), static_cast<float>(from.y()));
+ vertices[1].set(static_cast<float>(to.x()), static_cast<float>(to.y()));
+ node->markDirty(QSGNode::DirtyGeometry);
+ }
+
+ if (materialDirty) {
+ material->setColor(color);
+ node->markDirty(QSGNode::DirtyMaterial);
+ }
+
+ positionDirty = materialDirty = false;
+}
+
+void ChartLineItem::setLine(QPointF fromIn, QPointF toIn)
+{
+ from = fromIn;
+ to = toIn;
+ positionDirty = true;
+ dirty = true;
+}
diff --git a/stats/chartitem.h b/stats/chartitem.h
index 8cb9ef3a7..5dab8d1ea 100644
--- a/stats/chartitem.h
+++ b/stats/chartitem.h
@@ -7,6 +7,9 @@
#include <memory>
#include <QPainter>
+class QSGGeometry;
+class QSGGeometryNode;
+class QSGFlatColorMaterial;
class QSGImageNode;
class QSGTexture;
class StatsView;
@@ -15,31 +18,41 @@ enum class ChartZValue : int;
class ChartItem {
public:
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*.
- void setPos(QPointF pos);
- void render(); // Only call on render thread!
+ virtual ~ChartItem();
+ virtual void render() = 0; // Only call on render thread!
QRectF getRect() const;
bool dirty; // If true, call render() when rebuilding the scene
const ChartZValue zValue;
protected:
+ QSizeF sceneSize() const;
+ StatsView &view;
+};
+
+// A chart item that blits a precalculated pixmap onto the scene.
+class ChartPixmapItem : public ChartItem {
+public:
+ ChartPixmapItem(StatsView &v, ChartZValue z);
+ ~ChartPixmapItem();
+
+ void setPos(QPointF pos);
+ void render() override; // Only call on render thread!
+ QRectF getRect() const;
+protected:
+ void resize(QSizeF size); // Resets the canvas. Attention: image is *unitialized*.
std::unique_ptr<QPainter> painter;
std::unique_ptr<QImage> img;
- QSizeF sceneSize() const;
void setTextureDirty();
void setPositionDirty();
private:
- StatsView &view;
QRectF rect;
- bool positionDirty;
- bool textureDirty;
+ bool positionDirty; // true if the position changed since last render
+ bool textureDirty; // true if the pixmap changed since last render
std::unique_ptr<QSGImageNode> node;
std::unique_ptr<QSGTexture> texture;
};
// Draw a rectangular background after resize. Children are responsible for calling update().
-class ChartRectItem : public ChartItem {
+class ChartRectItem : public ChartPixmapItem {
public:
ChartRectItem(StatsView &v, ChartZValue z, const QPen &pen, const QBrush &brush, double radius);
~ChartRectItem();
@@ -50,4 +63,21 @@ private:
double radius;
};
+class ChartLineItem : public ChartItem {
+public:
+ ChartLineItem(StatsView &v, ChartZValue z, QColor color, double width);
+ ~ChartLineItem();
+ void setLine(QPointF from, QPointF to);
+ void render() override; // Only call on render thread!
+private:
+ QPointF from, to;
+ QColor color;
+ double width;
+ bool positionDirty;
+ bool materialDirty;
+ std::unique_ptr<QSGGeometryNode> node;
+ std::unique_ptr<QSGFlatColorMaterial> material;
+ std::unique_ptr<QSGGeometry> geometry;
+};
+
#endif
diff --git a/stats/quartilemarker.cpp b/stats/quartilemarker.cpp
new file mode 100644
index 000000000..ace019df5
--- /dev/null
+++ b/stats/quartilemarker.cpp
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "quartilemarker.h"
+#include "statsaxis.h"
+#include "zvalues.h"
+
+static const QColor quartileMarkerColor(Qt::red);
+static const double quartileMarkerSize = 15.0;
+
+QuartileMarker::QuartileMarker(StatsView &view, double pos, double value, StatsAxis *xAxis, StatsAxis *yAxis) :
+ ChartLineItem(view, ChartZValue::ChartFeatures, quartileMarkerColor, 2.0),
+ xAxis(xAxis), yAxis(yAxis),
+ pos(pos),
+ value(value)
+{
+ updatePosition();
+}
+
+QuartileMarker::~QuartileMarker()
+{
+}
+
+void QuartileMarker::updatePosition()
+{
+ if (!xAxis || !yAxis)
+ return;
+ double x = xAxis->toScreen(pos);
+ double y = yAxis->toScreen(value);
+ setLine(QPointF(x - quartileMarkerSize / 2.0, y),
+ QPointF(x + quartileMarkerSize / 2.0, y));
+}
diff --git a/stats/quartilemarker.h b/stats/quartilemarker.h
new file mode 100644
index 000000000..2e754248d
--- /dev/null
+++ b/stats/quartilemarker.h
@@ -0,0 +1,20 @@
+// A short line used to mark quartiles
+#ifndef QUARTILE_MARKER_H
+#define QUARTILE_MARKER_H
+
+#include "chartitem.h"
+
+class StatsAxis;
+class StatsView;
+
+class QuartileMarker : public ChartLineItem {
+public:
+ QuartileMarker(StatsView &view, double pos, double value, StatsAxis *xAxis, StatsAxis *yAxis);
+ ~QuartileMarker();
+ void updatePosition();
+private:
+ StatsAxis *xAxis, *yAxis;
+ double pos, value;
+};
+
+#endif
diff --git a/stats/statsview.cpp b/stats/statsview.cpp
index f07b00896..b4b82ccfa 100644
--- a/stats/statsview.cpp
+++ b/stats/statsview.cpp
@@ -4,6 +4,7 @@
#include "boxseries.h"
#include "legend.h"
#include "pieseries.h"
+#include "quartilemarker.h"
#include "scatterseries.h"
#include "statsaxis.h"
#include "statscolors.h"
@@ -25,8 +26,6 @@
#include <QSGTexture>
// Constants that control the graph layouts
-static const QColor quartileMarkerColor(Qt::red);
-static const double quartileMarkerSize = 15.0;
static const double sceneBorder = 5.0; // Border between scene edges and statitistics view
static const double titleBorder = 2.0; // Border between title and chart
@@ -211,8 +210,8 @@ void StatsView::plotAreaChanged(const QSizeF &s)
grid->updatePositions();
for (auto &series: series)
series->updatePositions();
- for (QuartileMarker &marker: quartileMarkers)
- marker.updatePosition();
+ for (auto &marker: quartileMarkers)
+ marker->updatePosition();
for (RegressionLine &line: regressionLines)
line.updatePosition();
for (HistogramMarker &marker: histogramMarkers)
@@ -799,36 +798,18 @@ void StatsView::plotDiscreteScatter(const std::vector<dive *> &dives,
if (quartiles) {
StatsQuartiles quartiles = StatsVariable::quartiles(array);
if (quartiles.isValid()) {
- quartileMarkers.emplace_back(x, quartiles.q1, &scene, catAxis, valAxis);
- quartileMarkers.emplace_back(x, quartiles.q2, &scene, catAxis, valAxis);
- quartileMarkers.emplace_back(x, quartiles.q3, &scene, catAxis, valAxis);
+ 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;
}
}
-StatsView::QuartileMarker::QuartileMarker(double pos, double value, QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis) :
- item(createItemPtr<QGraphicsLineItem>(scene)),
- xAxis(xAxis), yAxis(yAxis),
- pos(pos),
- value(value)
-{
- item->setZValue(ZValues::chartFeatures);
- item->setPen(QPen(quartileMarkerColor, 2.0));
- updatePosition();
-}
-
-void StatsView::QuartileMarker::updatePosition()
-{
- if (!xAxis || !yAxis)
- return;
- double x = xAxis->toScreen(pos);
- double y = yAxis->toScreen(value);
- item->setLine(x - quartileMarkerSize / 2.0, y,
- x + quartileMarkerSize / 2.0, y);
-}
-
StatsView::RegressionLine::RegressionLine(const struct regression_data reg, QBrush brush, QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis) :
item(createItemPtr<QGraphicsPolygonItem>(scene)),
central(createItemPtr<QGraphicsPolygonItem>(scene)),
diff --git a/stats/statsview.h b/stats/statsview.h
index 1a922ac98..073a200fd 100644
--- a/stats/statsview.h
+++ b/stats/statsview.h
@@ -24,6 +24,7 @@ class CategoryAxis;
class ChartItem;
class CountAxis;
class HistogramAxis;
+class QuartileMarker;
class StatsAxis;
class StatsGrid;
class Legend;
@@ -125,15 +126,6 @@ private:
// Helper functions to add feature to the chart
void addLineMarker(double pos, double low, double high, const QPen &pen, bool isHorizontal);
- // A short line used to mark quartiles
- struct QuartileMarker {
- std::unique_ptr<QGraphicsLineItem> item;
- StatsAxis *xAxis, *yAxis;
- double pos, value;
- QuartileMarker(double pos, double value, QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis);
- void updatePosition();
- };
-
// A regression line
struct RegressionLine {
std::unique_ptr<QGraphicsPolygonItem> item;
@@ -163,7 +155,7 @@ private:
std::unique_ptr<StatsGrid> grid;
std::vector<std::unique_ptr<StatsSeries>> series;
std::unique_ptr<Legend> legend;
- std::vector<QuartileMarker> quartileMarkers;
+ std::vector<std::unique_ptr<QuartileMarker>> quartileMarkers;
std::vector<RegressionLine> regressionLines;
std::vector<HistogramMarker> histogramMarkers;
std::unique_ptr<QGraphicsSimpleTextItem> title;