mirror of
https://github.com/subsurface/subsurface.git
synced 2024-12-11 03:21:29 +00:00
db516b6d4e
Fix the initial gasmix that is shown in the tank bar of the profile. Also add a meaningful gas name for gases with negative values for percentages. @bstoeger: This is a side effect of the `event_loop` functionality introduced as part of #4198. In the case of an `event_loop("gasmix")` this does not take into account the edge case where there is no gaschange event at the very beginning of the dive, and the first gasmix is implicitly used as the starting gasmix. This happens for planned and manually added dives, but also for some dive computers. We are using the same kind of loop in a number of other places in `core/profile.cpp`, `core/dive.cpp`, `core/gaspressures.cpp`, and `profile-widget/tankitem.cpp`, and I am wondering if we should be converting these to use `gasmix_loop` instead to avoid being bit by this special case? Signed-off-by: Michael Keller <github@ike.ch>
141 lines
4.2 KiB
C++
141 lines
4.2 KiB
C++
// SPDX-License-Identifier: GPL-2.0
|
|
#include "divepercentageitem.h"
|
|
#include "divecartesianaxis.h"
|
|
#include "core/dive.h"
|
|
#include "core/event.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::fromHsvF((float)h, (float)s, (float)v);
|
|
#else
|
|
QColor c = QColor::fromHsvF(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;
|
|
gasmix_loop loop(*d, *dc);
|
|
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 = loop.at(sec).first;
|
|
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);
|
|
}
|