subsurface/profile-widget/divepercentageitem.cpp
Dirk Hohndel 59fe2f3d7e cleanup: prevent distracing conversion warnings
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>
2022-03-12 08:28:32 -08:00

140 lines
4.2 KiB
C++

// SPDX-License-Identifier: GPL-2.0
#include "divepercentageitem.h"
#include "divecartesianaxis.h"
#include "core/dive.h"
#include "core/profile.h"
#include <array>
DivePercentageItem::DivePercentageItem(const DiveCartesianAxis &hAxis, const DiveCartesianAxis &vAxis) :
hAxis(hAxis), vAxis(vAxis)
{
}
static constexpr int num_tissues = 16;
// Calculate the number of scanlines for every drawn tissue.
static std::array<int, num_tissues> calcLinesPerTissue(int size)
{
std::array<int, num_tissues> res;
// A Bresenham-inspired algorithm without the weird half steps at the beginning and the end.
if (size <= 0) {
std::fill(res.begin(), res.end(), 0);
} else if (size >= num_tissues) {
int step = size / num_tissues;
int err_inc = size % num_tissues;
int err = 0;
for (int i = 0; i < num_tissues; ++i) {
res[i] = step;
err += err_inc;
if (err >= num_tissues) {
err -= num_tissues;
++res[i];
}
}
} else { // size < num_tissues
int step = num_tissues / size;
int err_inc = num_tissues % size;
int err = 0;
int act = 0;
std::fill(res.begin(), res.end(), 0);
for (int i = 0; i < size; ++i) {
res[act] = 1;
act += step;
err += err_inc;
if (err >= size) {
err -= size;
++act;
}
}
}
return res;
}
static inline QRgb hsv2rgb(double h, double s, double v)
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) // they are just trolling us with these changes
QColor c = QColor::fromHslF((float)h, (float)s, (float)v);
#else
QColor c = QColor::fromHslF(h, s, v);
#endif
return c.rgba();
}
static QRgb colorScale(double value, int inert)
{
double scaledValue = value / (AMB_PERCENTAGE * inert) * 1000.0;
if (scaledValue < 0.8) // grade from cyan to blue to purple
return hsv2rgb(0.5 + 0.25 * scaledValue / 0.8, 1.0, 1.0);
else if (scaledValue < 1.0) // grade from magenta to black
return hsv2rgb(0.75, 1.0, (1.0 - scaledValue) / 0.2);
else if (value < AMB_PERCENTAGE) // grade from black to bright green
return hsv2rgb(0.333, 1.0, (value - AMB_PERCENTAGE * inert / 1000.0) / (AMB_PERCENTAGE - AMB_PERCENTAGE * inert / 1000.0));
else if (value < 65) // grade from bright green (0% M) to yellow-green (30% M)
return hsv2rgb(0.333 - 0.133 * (value - AMB_PERCENTAGE) / (65.0 - AMB_PERCENTAGE), 1.0, 1.0);
else if (value < 85) // grade from yellow-green (30% M) to orange (70% M)
return hsv2rgb(0.2 - 0.1 * (value - 65.0) / 20.0, 1.0, 1.0);
else if (value < 100) // grade from orange (70% M) to red (100% M)
return hsv2rgb(0.1 * (100.0 - value) / 15.0, 1.0, 1.0);
else if (value < 120) // M value exceeded - grade from red to white
return hsv2rgb(0.0, 1 - (value - 100.0) / 20.0, 1.0);
else // white
return hsv2rgb(0.0, 0.0, 1.0);
}
void DivePercentageItem::replot(const dive *d, const struct divecomputer *dc, const plot_info &pi)
{
auto [minX, maxX] = hAxis.screenMinMax();
auto [minY, maxY] = vAxis.screenMinMax();
int width = lrint(maxX) - lrint(minX);
int height = lrint(maxY) - lrint(minY);
if (width <= 0 || height <= 0) {
setPixmap(QPixmap());
return;
}
std::array<int, num_tissues> linesPerTissue = calcLinesPerTissue(height);
QImage img(width, height, QImage::QImage::Format_ARGB32);
int line = 0;
for (int tissue = 0; tissue < num_tissues; ++tissue) {
if (linesPerTissue[tissue] <= 0)
continue;
int x = 0;
QRgb *scanline = (QRgb *)img.scanLine(line);
QRgb color = 0;
const struct event *ev = NULL;
for (int i = 0; i < pi.nr; i++) {
const plot_data &item = pi.entry[i];
int sec = item.sec;
int nextX = lrint(hAxis.posAtValue(sec)) - lrint(minX);
if (nextX == x)
continue;
double value = item.percentages[tissue];
struct gasmix gasmix = get_gasmix(d, dc, sec, &ev, gasmix);
int inert = get_n2(gasmix) + get_he(gasmix);
color = colorScale(value, inert);
if (nextX >= width)
nextX = width - 1;
for (; x <= nextX; ++x)
scanline[x] = color;
if (nextX >= width - 1)
break;
}
for (; x < width; ++x)
scanline[x] = color;
++line;
// Clone line if needed
for (int i = 0; i < linesPerTissue[tissue] - 1; ++i) {
QRgb *scanline2 = (QRgb *)img.scanLine(line);
std::copy(scanline, scanline + width, scanline2);
++line;
}
}
setPixmap(QPixmap::fromImage(img));
setPos(minX, minY);
}