mirror of
https://github.com/subsurface/subsurface.git
synced 2025-01-19 06:15:26 +00:00
statistics: convert QuartileMarkers to QSGNodes
Slowly converting the QGraphicsScene items to QSGNodes to avoid full replot of the scene. This adds a new abstraction for line-nodes. Since the render() function here is fundamentally different from the pixmap-nodes we had so far, this has to be made virtual. Also, move the quartile markers to their own source file, since the StatsView source file is quite huge already. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
parent
b1c0d42408
commit
790d2b2ddb
8 changed files with 172 additions and 58 deletions
|
@ -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 \
|
||||
|
|
|
@ -19,6 +19,8 @@ set(SUBSURFACE_STATS_SRCS
|
|||
legend.cpp
|
||||
pieseries.h
|
||||
pieseries.cpp
|
||||
quartilemarker.h
|
||||
quartilemarker.cpp
|
||||
scatterseries.h
|
||||
scatterseries.cpp
|
||||
statsaxis.h
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
30
stats/quartilemarker.cpp
Normal file
30
stats/quartilemarker.cpp
Normal file
|
@ -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));
|
||||
}
|
20
stats/quartilemarker.h
Normal file
20
stats/quartilemarker.h
Normal file
|
@ -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
|
|
@ -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)),
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue