mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-19 22:16:15 +00:00
statistics: draw legend as a QSGNode
In order not to waste CPU by constantly rerendering the chart, we must use these weird OpenGL QSGNode things. The interface is appallingly low-level and unfriendly. As a first test, try to convert the legend. Create a wrapper class that represents a rectangular item with a texture and that will certainly need some (lots of) optimization. Make sure that all low-level QSG-objects are only accessed in the rendering thread. This means that the wrapper has to maintain a notion of "dirtiness" of the state. I.e. which part of the QSG-objects have to be modified. From the low-level wrapper derive a class that draws a rounded rectangle for every resize. The child class of that must then paint on the rectangle after every resize. That looks all not very fortunate, but it displays a legend and will make it possible to move the legend without and drawing operations, only shifting around an OpenGL surface. The render thread goes through all chart-items and rerenders them if dirty. Currently, on deletion of these items, this list is not reset. I.e. currently it is not supported to remove individual items. Only the full scene can be cleared! Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
parent
9be23b5f3f
commit
2b961414d7
8 changed files with 284 additions and 91 deletions
|
|
@ -25,28 +25,6 @@
|
|||
#include <QSGImageNode>
|
||||
#include <QSGTexture>
|
||||
|
||||
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 = window()->createImageNode();
|
||||
|
||||
QRectF rect = boundingRect();
|
||||
if (plotRect != rect) {
|
||||
plotRect = rect;
|
||||
plotAreaChanged(plotRect.size());
|
||||
}
|
||||
|
||||
img->fill(backgroundColor);
|
||||
scene.render(painter.get());
|
||||
texture.reset(window()->createTextureFromImage(*img, QQuickWindow::TextureIsOpaque));
|
||||
n->setTexture(texture.get());
|
||||
n->setRect(rect);
|
||||
return n;
|
||||
}
|
||||
|
||||
// Constants that control the graph layouts
|
||||
static const QColor quartileMarkerColor(Qt::red);
|
||||
static const double quartileMarkerSize = 15.0;
|
||||
|
|
@ -56,7 +34,8 @@ static const double titleBorder = 2.0; // Border between title and chart
|
|||
StatsView::StatsView(QQuickItem *parent) : QQuickItem(parent),
|
||||
highlightedSeries(nullptr),
|
||||
xAxis(nullptr),
|
||||
yAxis(nullptr)
|
||||
yAxis(nullptr),
|
||||
rootNode(nullptr)
|
||||
{
|
||||
setFlag(ItemHasContents, true);
|
||||
|
||||
|
|
@ -76,6 +55,59 @@ StatsView::~StatsView()
|
|||
{
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
QRectF rect = boundingRect();
|
||||
if (plotRect != rect) {
|
||||
plotRect = rect;
|
||||
plotAreaChanged(plotRect.size());
|
||||
}
|
||||
|
||||
img->fill(backgroundColor);
|
||||
scene.render(painter.get());
|
||||
texture.reset(window()->createTextureFromImage(*img, QQuickWindow::TextureIsOpaque));
|
||||
n->setTexture(texture.get());
|
||||
n->setRect(rect);
|
||||
return n;
|
||||
}
|
||||
|
||||
void StatsView::addQSGNode(QSGNode *node, int)
|
||||
{
|
||||
rootNode->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.
|
||||
void StatsView::unregisterChartItem(const ChartItem *item)
|
||||
{
|
||||
auto it = std::find(items.begin(), items.end(), item);
|
||||
if (it != items.end())
|
||||
items.erase(it);
|
||||
}
|
||||
|
||||
QQuickWindow *StatsView::w() const
|
||||
{
|
||||
return window();
|
||||
}
|
||||
|
||||
QSizeF StatsView::size() const
|
||||
{
|
||||
return boundingRect().size();
|
||||
}
|
||||
|
||||
void StatsView::plotAreaChanged(const QSizeF &s)
|
||||
{
|
||||
// Make sure that image is at least one pixel wide / high, otherwise
|
||||
|
|
@ -202,6 +234,14 @@ T *StatsView::createAxis(const QString &title, Args&&... args)
|
|||
return res;
|
||||
}
|
||||
|
||||
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());
|
||||
return res;
|
||||
}
|
||||
|
||||
void StatsView::setAxes(StatsAxis *x, StatsAxis *y)
|
||||
{
|
||||
xAxis = x;
|
||||
|
|
@ -214,6 +254,7 @@ void StatsView::reset()
|
|||
{
|
||||
highlightedSeries = nullptr;
|
||||
xAxis = yAxis = nullptr;
|
||||
items.clear(); // non-owning pointers
|
||||
legend.reset();
|
||||
series.clear();
|
||||
quartileMarkers.clear();
|
||||
|
|
@ -406,7 +447,7 @@ void StatsView::plotBarChart(const std::vector<dive *> &dives,
|
|||
|
||||
// Paint legend first, because the bin-names will be moved away from.
|
||||
if (showLegend)
|
||||
legend = createItemPtr<Legend>(&scene, data.vbinNames);
|
||||
legend = createChartItem<Legend>(data.vbinNames);
|
||||
|
||||
std::vector<BarSeries::MultiItem> items;
|
||||
items.reserve(data.hbin_counts.size());
|
||||
|
|
@ -623,7 +664,7 @@ void StatsView::plotPieChart(const std::vector<dive *> &dives,
|
|||
PieSeries *series = createSeries<PieSeries>(categoryVariable->name(), data, keepOrder, labels);
|
||||
|
||||
if (showLegend)
|
||||
legend = createItemPtr<Legend>(&scene, series->binNames());
|
||||
legend = createChartItem<Legend>(series->binNames());
|
||||
}
|
||||
|
||||
void StatsView::plotDiscreteBoxChart(const std::vector<dive *> &dives,
|
||||
|
|
@ -959,7 +1000,7 @@ void StatsView::plotHistogramStackedChart(const std::vector<dive *> &dives,
|
|||
|
||||
BarPlotData data(categoryBins, *valueBinner);
|
||||
if (showLegend)
|
||||
legend = createItemPtr<Legend>(&scene, data.vbinNames);
|
||||
legend = createChartItem<Legend>(data.vbinNames);
|
||||
|
||||
CountAxis *valAxis = createCountAxis(data.maxCategoryCount, isHorizontal);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue