mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-19 22:16:15 +00:00
statistics: render regression item using QSGNode
Render the confidence area and the regression line into a pixmap and show that using a QSGNode. It is unclear whether it is preferred to do it this way or to triangulate the confidence area into triangles to be drawn by the shader. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
parent
faf3e7079d
commit
2008857660
6 changed files with 124 additions and 75 deletions
87
stats/regressionitem.cpp
Normal file
87
stats/regressionitem.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "regressionitem.h"
|
||||
#include "statsaxis.h"
|
||||
#include "zvalues.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
static const QColor regressionItemColor(Qt::red);
|
||||
static const double regressionLineWidth = 2.0;
|
||||
|
||||
RegressionItem::RegressionItem(StatsView &view, regression_data reg,
|
||||
StatsAxis *xAxis, StatsAxis *yAxis) :
|
||||
ChartPixmapItem(view, ChartZValue::ChartFeatures),
|
||||
xAxis(xAxis), yAxis(yAxis), reg(reg)
|
||||
{
|
||||
}
|
||||
|
||||
RegressionItem::~RegressionItem()
|
||||
{
|
||||
}
|
||||
|
||||
void RegressionItem::updatePosition()
|
||||
{
|
||||
if (!xAxis || !yAxis)
|
||||
return;
|
||||
auto [minX, maxX] = xAxis->minMax();
|
||||
auto [minY, maxY] = yAxis->minMax();
|
||||
auto [screenMinX, screenMaxX] = xAxis->minMaxScreen();
|
||||
|
||||
// Draw the confidence interval according to http://www2.stat.duke.edu/~tjl13/s101/slides/unit6lec3H.pdf p.5 with t*=2 for 95% confidence
|
||||
QPolygonF poly;
|
||||
const int num_samples = 101;
|
||||
poly.reserve(num_samples * 2);
|
||||
for (int i = 0; i < num_samples; ++i) {
|
||||
double x = (maxX - minX) / (num_samples - 1) * static_cast<double>(i) + minX;
|
||||
poly << QPointF(xAxis->toScreen(x),
|
||||
yAxis->toScreen(reg.a * x + reg.b + 2.0 * sqrt(reg.res2 / (reg.n - 2) * (1.0 / reg.n + (x - reg.xavg) * (x - reg.xavg) / (reg.n - 1) * (reg.n -2) / reg.sx2))));
|
||||
}
|
||||
for (int i = num_samples - 1; i >= 0; --i) {
|
||||
double x = (maxX - minX) / (num_samples - 1) * static_cast<double>(i) + minX;
|
||||
poly << QPointF(xAxis->toScreen(x),
|
||||
yAxis->toScreen(reg.a * x + reg.b - 2.0 * sqrt(reg.res2 / (reg.n - 2) * (1.0 / reg.n + (x - reg.xavg) * (x - reg.xavg) / (reg.n - 1) * (reg.n -2) / reg.sx2))));
|
||||
}
|
||||
QPolygonF linePolygon;
|
||||
linePolygon.reserve(2);
|
||||
linePolygon << QPointF(screenMinX, yAxis->toScreen(reg.a * minX + reg.b));
|
||||
linePolygon << QPointF(screenMaxX, yAxis->toScreen(reg.a * maxX + reg.b));
|
||||
|
||||
QRectF box(QPointF(screenMinX, yAxis->toScreen(minY)), QPointF(screenMaxX, yAxis->toScreen(maxY)));
|
||||
|
||||
poly = poly.intersected(box);
|
||||
linePolygon = linePolygon.intersected(box);
|
||||
if (poly.size() < 2 || linePolygon.size() < 2)
|
||||
return;
|
||||
|
||||
// Find lowest and highest point on screen. In principle, we need
|
||||
// only check half of the polygon, but let's not optimize without reason.
|
||||
double screenMinY = std::numeric_limits<double>::max();
|
||||
double screenMaxY = std::numeric_limits<double>::lowest();
|
||||
for (const QPointF &point: poly) {
|
||||
double y = point.y();
|
||||
if (y < screenMinY)
|
||||
screenMinY = y;
|
||||
if (y > screenMaxY)
|
||||
screenMaxY = y;
|
||||
}
|
||||
screenMinY = floor(screenMinY - 1.0);
|
||||
screenMaxY = ceil(screenMaxY + 1.0);
|
||||
QPointF offset(screenMinX, screenMinY);
|
||||
for (QPointF &point: poly)
|
||||
point -= offset;
|
||||
for (QPointF &point: linePolygon)
|
||||
point -= offset;
|
||||
ChartPixmapItem::resize(QSizeF(screenMaxX - screenMinX, screenMaxY - screenMinY));
|
||||
|
||||
img->fill(Qt::transparent);
|
||||
QColor col(regressionItemColor);
|
||||
col.setAlphaF(reg.r2);
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->setBrush(QBrush(col));
|
||||
painter->drawPolygon(poly);
|
||||
|
||||
painter->setPen(QPen(regressionItemColor, regressionLineWidth));
|
||||
painter->drawLine(QPointF(linePolygon[0]), QPointF(linePolygon[1]));
|
||||
|
||||
ChartPixmapItem::setPos(offset);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue