mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-30 22:20:21 +00:00
59fe2f3d7e
We do want the -Wfloat-conversion warnings where they point out potential bugs. But they are very distracting when they are triggered by floating point literals (which the standard defines as double) passed to a function expecting float arguments. The fact that Qt6 changes the arguments to all these functions from double to float is... hard to explain, but it is what it is. With these changes, for the majority of cases we create inlined helpers that conditionally compile to do the right thing. And in a handful of other cases we simply cast to float (and accept that on Qt5 this then gets cast back to double... for none of these cases the potential loss in precision makes any difference, anyway - which likely is why the Qt community made the decision to change the type of the arguments in the first place). Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
102 lines
3.4 KiB
C++
102 lines
3.4 KiB
C++
// SPDX-License-Identifier: GPL-2.0
|
|
#include "regressionitem.h"
|
|
#include "statsaxis.h"
|
|
#include "statscolors.h"
|
|
#include "zvalues.h"
|
|
|
|
#include <cmath>
|
|
|
|
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),
|
|
regression(true), confidence(true)
|
|
{
|
|
}
|
|
|
|
RegressionItem::~RegressionItem()
|
|
{
|
|
}
|
|
|
|
void RegressionItem::setFeatures(bool regressionIn, bool confidenceIn)
|
|
{
|
|
if (regressionIn == regression && confidenceIn == confidence)
|
|
return;
|
|
regression = regressionIn;
|
|
confidence = confidenceIn;
|
|
updatePosition();
|
|
}
|
|
|
|
// Note: this calculates the confidence area, even if it isn't shown. Might want to optimize this.
|
|
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 + 1.960 * 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 - 1.960 * 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);
|
|
if (confidence) {
|
|
QColor col(regressionItemColor);
|
|
col.setAlphaF((float)reg.r2);
|
|
painter->setPen(Qt::NoPen);
|
|
painter->setBrush(QBrush(col));
|
|
painter->drawPolygon(poly);
|
|
}
|
|
|
|
if (regression) {
|
|
painter->setPen(QPen(regressionItemColor, regressionLineWidth));
|
|
painter->drawLine(QPointF(linePolygon[0]), QPointF(linePolygon[1]));
|
|
}
|
|
|
|
ChartPixmapItem::setPos(offset);
|
|
}
|