diff options
author | Berthold Stoeger <bstoeger@mail.tuwien.ac.at> | 2021-01-15 18:39:14 +0100 |
---|---|---|
committer | bstoeger <32835590+bstoeger@users.noreply.github.com> | 2021-01-20 08:47:18 +0100 |
commit | 20088576604a3159cc9891bcfea888c15b096a96 (patch) | |
tree | 244e1996c21adb6071346d754e5ac401d726aa15 /stats/regressionitem.cpp | |
parent | faf3e7079ddd680ab0e53a27a7ddc0d863792117 (diff) | |
download | subsurface-20088576604a3159cc9891bcfea888c15b096a96.tar.gz |
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>
Diffstat (limited to 'stats/regressionitem.cpp')
-rw-r--r-- | stats/regressionitem.cpp | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/stats/regressionitem.cpp b/stats/regressionitem.cpp new file mode 100644 index 000000000..db02cf688 --- /dev/null +++ b/stats/regressionitem.cpp @@ -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); +} |