2017-04-27 18:26:36 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2016-04-05 05:02:03 +00:00
|
|
|
#include "profile-widget/diveprofileitem.h"
|
|
|
|
#include "qt-models/diveplotdatamodel.h"
|
|
|
|
#include "profile-widget/divecartesianaxis.h"
|
|
|
|
#include "profile-widget/divetextitem.h"
|
|
|
|
#include "profile-widget/animationfunctions.h"
|
|
|
|
#include "core/profile.h"
|
2016-03-06 14:52:55 +00:00
|
|
|
#include "qt-models/diveplannermodel.h"
|
2018-06-03 20:15:19 +00:00
|
|
|
#include "core/qthelper.h"
|
2018-08-15 09:52:50 +00:00
|
|
|
#include "core/settings/qPrefTechnicalDetails.h"
|
2019-12-09 18:58:20 +00:00
|
|
|
#include "core/settings/qPrefLog.h"
|
2014-07-11 17:26:22 +00:00
|
|
|
#include "libdivecomputer/parser.h"
|
2016-04-05 05:02:03 +00:00
|
|
|
#include "profile-widget/profilewidget2.h"
|
2014-01-14 19:17:17 +00:00
|
|
|
|
2020-12-20 17:12:55 +00:00
|
|
|
AbstractProfilePolygonItem::AbstractProfilePolygonItem(const DivePlotDataModel &model, const DiveCartesianAxis &horizontal, int hColumn,
|
2021-08-09 14:48:08 +00:00
|
|
|
const DiveCartesianAxis &vertical, int vColumn, double dpr) :
|
2021-10-08 19:00:37 +00:00
|
|
|
hAxis(horizontal), vAxis(vertical), dataModel(model), hDataColumn(hColumn), vDataColumn(vColumn), dpr(dpr), from(0), to(0)
|
2014-01-14 19:17:17 +00:00
|
|
|
{
|
2015-01-16 20:50:28 +00:00
|
|
|
setCacheMode(DeviceCoordinateCache);
|
2014-01-21 20:16:19 +00:00
|
|
|
}
|
|
|
|
|
2020-12-24 18:21:30 +00:00
|
|
|
void AbstractProfilePolygonItem::clear()
|
|
|
|
{
|
|
|
|
setPolygon(QPolygonF());
|
|
|
|
qDeleteAll(texts);
|
|
|
|
texts.clear();
|
|
|
|
}
|
|
|
|
|
2021-10-09 12:56:12 +00:00
|
|
|
static std::pair<double,double> clip(double x1, double y1, double x2, double y2, double x)
|
|
|
|
{
|
|
|
|
double rel = fabs(x2 - x1) > 1e-10 ? (x - x1) / (x2 - x1) : 0.5;
|
|
|
|
return { x, (y2 - y1) * rel + y1 };
|
|
|
|
}
|
|
|
|
|
|
|
|
void AbstractProfilePolygonItem::clipStart(double &x, double &y, double next_x, double next_y) const
|
|
|
|
{
|
|
|
|
if (x < hAxis.minimum())
|
|
|
|
std::tie(x, y) = clip(x, y, next_x, next_y, hAxis.minimum());
|
|
|
|
}
|
|
|
|
|
|
|
|
void AbstractProfilePolygonItem::clipStop(double &x, double &y, double prev_x, double prev_y) const
|
|
|
|
{
|
|
|
|
if (x > hAxis.maximum())
|
|
|
|
std::tie(x, y) = clip(prev_x, prev_y, x, y, hAxis.maximum());
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<double, double> AbstractProfilePolygonItem::getPoint(int i) const
|
|
|
|
{
|
|
|
|
double x = dataModel.index(i, hDataColumn).data().toReal();
|
|
|
|
double y = dataModel.index(i, vDataColumn).data().toReal();
|
|
|
|
|
|
|
|
// Do clipping of first and last value
|
|
|
|
if (i == from && i < to) {
|
|
|
|
double next_x = dataModel.index(i+1, hDataColumn).data().toReal();
|
|
|
|
double next_y = dataModel.index(i+1, vDataColumn).data().toReal();
|
|
|
|
clipStart(x, y, next_x, next_y);
|
|
|
|
}
|
|
|
|
if (i == to - 1 && i > 0) {
|
|
|
|
double prev_x = dataModel.index(i-1, hDataColumn).data().toReal();
|
|
|
|
double prev_y = dataModel.index(i-1, vDataColumn).data().toReal();
|
|
|
|
clipStop(x, y, prev_x, prev_y);
|
|
|
|
}
|
|
|
|
|
|
|
|
return { x, y };
|
|
|
|
}
|
|
|
|
|
2021-10-08 19:00:37 +00:00
|
|
|
void AbstractProfilePolygonItem::makePolygon(int fromIn, int toIn)
|
2014-01-14 19:17:17 +00:00
|
|
|
{
|
2021-10-08 19:00:37 +00:00
|
|
|
from = fromIn;
|
|
|
|
to = toIn;
|
|
|
|
|
2014-01-14 19:17:17 +00:00
|
|
|
// Calculate the polygon. This is the polygon that will be painted on screen
|
|
|
|
// on the ::paint method. Here we calculate the correct position of the points
|
|
|
|
// regarting our cartesian plane ( made by the hAxis and vAxis ), the QPolygonF
|
|
|
|
// is an array of QPointF's, so we basically get the point from the model, convert
|
|
|
|
// to our coordinates, store. no painting is done here.
|
|
|
|
QPolygonF poly;
|
2021-10-08 19:00:37 +00:00
|
|
|
for (int i = from; i < to; i++) {
|
2021-10-09 12:56:12 +00:00
|
|
|
auto [horizontalValue, verticalValue] = getPoint(i);
|
|
|
|
|
2021-10-08 19:00:37 +00:00
|
|
|
if (i == from) {
|
|
|
|
QPointF point(hAxis.posAtValue(horizontalValue), vAxis.posAtValue(0.0));
|
|
|
|
poly.append(point);
|
|
|
|
}
|
2020-12-20 17:12:55 +00:00
|
|
|
QPointF point(hAxis.posAtValue(horizontalValue), vAxis.posAtValue(verticalValue));
|
2014-01-14 19:17:17 +00:00
|
|
|
poly.append(point);
|
2021-10-08 19:00:37 +00:00
|
|
|
if (i == to - 1) {
|
|
|
|
QPointF point(hAxis.posAtValue(horizontalValue), vAxis.posAtValue(0.0));
|
|
|
|
poly.append(point);
|
|
|
|
}
|
2014-01-14 19:17:17 +00:00
|
|
|
}
|
|
|
|
setPolygon(poly);
|
2014-01-21 16:05:29 +00:00
|
|
|
|
|
|
|
qDeleteAll(texts);
|
|
|
|
texts.clear();
|
2014-01-14 19:17:17 +00:00
|
|
|
}
|
|
|
|
|
2021-06-03 15:19:38 +00:00
|
|
|
DiveProfileItem::DiveProfileItem(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, int hColumn,
|
2021-08-09 14:48:08 +00:00
|
|
|
const DiveCartesianAxis &vAxis, int vColumn, double dpr) :
|
|
|
|
AbstractProfilePolygonItem(model, hAxis, hColumn, vAxis, vColumn, dpr),
|
2021-01-02 17:45:10 +00:00
|
|
|
show_reported_ceiling(0), reported_ceiling_in_red(0)
|
2014-02-09 17:54:25 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-02-28 04:09:57 +00:00
|
|
|
void DiveProfileItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
|
|
|
{
|
|
|
|
if (polygon().isEmpty())
|
2014-02-10 16:41:59 +00:00
|
|
|
return;
|
2014-01-14 19:17:17 +00:00
|
|
|
|
2014-07-17 23:18:14 +00:00
|
|
|
painter->save();
|
2014-01-14 19:17:17 +00:00
|
|
|
// This paints the Polygon + Background. I'm setting the pen to QPen() so we don't get a black line here,
|
|
|
|
// after all we need to plot the correct velocities colors later.
|
2014-01-21 19:07:22 +00:00
|
|
|
setPen(Qt::NoPen);
|
2014-01-14 19:17:17 +00:00
|
|
|
QGraphicsPolygonItem::paint(painter, option, widget);
|
|
|
|
|
|
|
|
// Here we actually paint the boundaries of the Polygon using the colors that the model provides.
|
|
|
|
// Those are the speed colors of the dives.
|
|
|
|
QPen pen;
|
|
|
|
pen.setCosmetic(true);
|
|
|
|
pen.setWidth(2);
|
2014-02-05 17:45:23 +00:00
|
|
|
QPolygonF poly = polygon();
|
2014-01-14 19:17:17 +00:00
|
|
|
// This paints the colors of the velocities.
|
2021-10-08 19:00:37 +00:00
|
|
|
for (int i = from + 1; i < to; i++) {
|
2021-01-02 17:45:10 +00:00
|
|
|
QModelIndex colorIndex = dataModel.index(i, DivePlotDataModel::COLOR);
|
2014-01-14 19:17:17 +00:00
|
|
|
pen.setBrush(QBrush(colorIndex.data(Qt::BackgroundRole).value<QColor>()));
|
|
|
|
painter->setPen(pen);
|
2021-10-08 19:00:37 +00:00
|
|
|
if (i - from < poly.count() - 1)
|
|
|
|
painter->drawLine(poly[i - from], poly[i - from + 1]);
|
2014-01-14 19:17:17 +00:00
|
|
|
}
|
2014-07-17 23:18:14 +00:00
|
|
|
painter->restore();
|
2014-01-14 19:17:17 +00:00
|
|
|
}
|
2014-01-16 18:21:23 +00:00
|
|
|
|
2021-10-08 19:00:37 +00:00
|
|
|
void DiveProfileItem::replot(const dive *d, int from, int to, bool in_planner)
|
2014-01-21 22:27:44 +00:00
|
|
|
{
|
2021-10-08 19:00:37 +00:00
|
|
|
makePolygon(from, to);
|
2014-01-21 22:27:44 +00:00
|
|
|
if (polygon().isEmpty())
|
2014-01-16 18:21:23 +00:00
|
|
|
return;
|
2014-01-21 19:07:22 +00:00
|
|
|
|
2014-04-16 20:03:44 +00:00
|
|
|
show_reported_ceiling = prefs.dcceiling;
|
|
|
|
reported_ceiling_in_red = prefs.redceiling;
|
2021-01-09 16:28:23 +00:00
|
|
|
profileColor = dataModel.data().waypoint_above_ceiling ? QColor(Qt::red)
|
|
|
|
: getColor(DEPTH_BOTTOM);
|
2014-05-26 22:29:25 +00:00
|
|
|
|
2014-01-21 19:07:22 +00:00
|
|
|
/* Show any ceiling we may have encountered */
|
2014-04-16 20:03:44 +00:00
|
|
|
if (prefs.dcceiling && !prefs.redceiling) {
|
2014-01-21 19:07:22 +00:00
|
|
|
QPolygonF p = polygon();
|
2021-10-08 19:00:37 +00:00
|
|
|
plot_data *entry = dataModel.data().entry + to - 1;
|
|
|
|
for (int i = to - 1; i >= from; i--, entry--) {
|
2014-01-21 19:07:22 +00:00
|
|
|
if (!entry->in_deco) {
|
|
|
|
/* not in deco implies this is a safety stop, no ceiling */
|
2020-12-20 17:12:55 +00:00
|
|
|
p.append(QPointF(hAxis.posAtValue(entry->sec), vAxis.posAtValue(0)));
|
2014-05-22 18:40:22 +00:00
|
|
|
} else {
|
2020-12-20 17:12:55 +00:00
|
|
|
p.append(QPointF(hAxis.posAtValue(entry->sec), vAxis.posAtValue(qMin(entry->stopdepth, entry->depth))));
|
2014-01-21 19:07:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
setPolygon(p);
|
|
|
|
}
|
|
|
|
|
2014-02-28 04:09:57 +00:00
|
|
|
// This is the blueish gradient that the Depth Profile should have.
|
2014-01-16 18:21:23 +00:00
|
|
|
// It's a simple QLinearGradient with 2 stops, starting from top to bottom.
|
|
|
|
QLinearGradient pat(0, polygon().boundingRect().top(), 0, polygon().boundingRect().bottom());
|
2014-05-26 22:29:25 +00:00
|
|
|
pat.setColorAt(1, profileColor);
|
2014-01-16 18:21:23 +00:00
|
|
|
pat.setColorAt(0, getColor(DEPTH_TOP));
|
2014-01-21 22:27:44 +00:00
|
|
|
setBrush(QBrush(pat));
|
2014-01-21 15:27:08 +00:00
|
|
|
|
|
|
|
int last = -1;
|
2021-10-08 19:00:37 +00:00
|
|
|
for (int i = from; i < to; i++) {
|
2021-01-02 17:45:10 +00:00
|
|
|
struct plot_data *pd = dataModel.data().entry;
|
2016-04-20 22:12:53 +00:00
|
|
|
struct plot_data *entry = pd + i;
|
2016-04-20 22:55:51 +00:00
|
|
|
// "min/max" are the 9-minute window min/max indices
|
|
|
|
struct plot_data *min_entry = pd + entry->min;
|
|
|
|
struct plot_data *max_entry = pd + entry->max;
|
2014-01-21 15:27:08 +00:00
|
|
|
|
|
|
|
if (entry->depth < 2000)
|
|
|
|
continue;
|
|
|
|
|
2016-04-20 22:12:53 +00:00
|
|
|
if ((entry == max_entry) && entry->depth / 100 != last) {
|
2014-01-29 15:48:06 +00:00
|
|
|
plot_depth_sample(entry, Qt::AlignHCenter | Qt::AlignBottom, getColor(SAMPLE_DEEP));
|
2014-01-21 15:27:08 +00:00
|
|
|
last = entry->depth / 100;
|
|
|
|
}
|
|
|
|
|
2016-04-20 22:12:53 +00:00
|
|
|
if ((entry == min_entry) && entry->depth / 100 != last) {
|
2014-01-29 15:48:06 +00:00
|
|
|
plot_depth_sample(entry, Qt::AlignHCenter | Qt::AlignTop, getColor(SAMPLE_SHALLOW));
|
2014-01-21 15:27:08 +00:00
|
|
|
last = entry->depth / 100;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entry->depth != last)
|
|
|
|
last = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-28 04:09:57 +00:00
|
|
|
void DiveProfileItem::plot_depth_sample(struct plot_data *entry, QFlags<Qt::AlignmentFlag> flags, const QColor &color)
|
2014-01-21 15:27:08 +00:00
|
|
|
{
|
2021-08-12 20:57:57 +00:00
|
|
|
DiveTextItem *item = new DiveTextItem(dpr, 1.0, flags, this);
|
2021-08-13 07:56:46 +00:00
|
|
|
item->set(get_depth_string(entry->depth, true), color);
|
2020-12-20 17:12:55 +00:00
|
|
|
item->setPos(hAxis.posAtValue(entry->sec), vAxis.posAtValue(entry->depth));
|
2014-01-21 15:27:08 +00:00
|
|
|
texts.append(item);
|
2014-01-16 20:39:13 +00:00
|
|
|
}
|
|
|
|
|
2021-06-03 15:19:38 +00:00
|
|
|
DiveHeartrateItem::DiveHeartrateItem(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, int hColumn,
|
2021-08-09 14:48:08 +00:00
|
|
|
const DiveCartesianAxis &vAxis, int vColumn, double dpr) :
|
|
|
|
AbstractProfilePolygonItem(model, hAxis, hColumn, vAxis, vColumn, dpr)
|
2014-02-23 23:28:31 +00:00
|
|
|
{
|
|
|
|
QPen pen;
|
|
|
|
pen.setBrush(QBrush(getColor(::HR_PLOT)));
|
|
|
|
pen.setCosmetic(true);
|
|
|
|
pen.setWidth(1);
|
|
|
|
setPen(pen);
|
|
|
|
}
|
|
|
|
|
2021-10-08 19:00:37 +00:00
|
|
|
void DiveHeartrateItem::replot(const dive *, int fromIn, int toIn, bool)
|
2014-02-23 23:28:31 +00:00
|
|
|
{
|
2021-10-08 19:00:37 +00:00
|
|
|
from = fromIn;
|
|
|
|
to = toIn;
|
|
|
|
|
2021-10-09 12:56:12 +00:00
|
|
|
int last = -300, last_printed_hr = 0;
|
2021-08-12 20:57:57 +00:00
|
|
|
struct sec_hr {
|
2014-02-25 22:24:09 +00:00
|
|
|
int sec;
|
|
|
|
int hr;
|
2014-03-10 11:43:44 +00:00
|
|
|
} hist[3] = {};
|
2021-08-12 20:57:57 +00:00
|
|
|
std::vector<sec_hr> textItems;
|
2014-02-25 22:24:09 +00:00
|
|
|
|
2014-02-23 23:28:31 +00:00
|
|
|
qDeleteAll(texts);
|
|
|
|
texts.clear();
|
2017-02-20 08:29:20 +00:00
|
|
|
// Ignore empty values. a heart rate of 0 would be a bad sign.
|
2014-02-23 23:28:31 +00:00
|
|
|
QPolygonF poly;
|
2021-10-08 19:00:37 +00:00
|
|
|
for (int i = from; i < to; i++) {
|
2021-10-09 12:56:12 +00:00
|
|
|
auto [sec_double, hr_double] = getPoint(i);
|
|
|
|
int hr = lrint(hr_double);
|
2014-02-23 23:28:31 +00:00
|
|
|
if (!hr)
|
|
|
|
continue;
|
2021-10-09 12:56:12 +00:00
|
|
|
int sec = lrint(sec_double);
|
|
|
|
QPointF point(hAxis.posAtValue(sec_double), vAxis.posAtValue(hr_double));
|
2014-02-23 23:28:31 +00:00
|
|
|
poly.append(point);
|
2014-02-25 22:24:09 +00:00
|
|
|
if (hr == hist[2].hr)
|
|
|
|
// same as last one, no point in looking at printing
|
|
|
|
continue;
|
|
|
|
hist[0] = hist[1];
|
|
|
|
hist[1] = hist[2];
|
|
|
|
hist[2].sec = sec;
|
|
|
|
hist[2].hr = hr;
|
|
|
|
// don't print a HR
|
|
|
|
// if it's not a local min / max
|
|
|
|
// if it's been less than 5min and less than a 20 beats change OR
|
|
|
|
// if it's been less than 2min OR if the change from the
|
|
|
|
// last print is less than 10 beats
|
|
|
|
// to test min / max requires three points, so we now look at the
|
|
|
|
// previous one
|
|
|
|
sec = hist[1].sec;
|
|
|
|
hr = hist[1].hr;
|
|
|
|
if ((hist[0].hr < hr && hr < hist[2].hr) ||
|
|
|
|
(hist[0].hr > hr && hr > hist[2].hr) ||
|
|
|
|
((sec < last + 300) && (abs(hr - last_printed_hr) < 20)) ||
|
2014-02-23 23:28:31 +00:00
|
|
|
(sec < last + 120) ||
|
|
|
|
(abs(hr - last_printed_hr) < 10))
|
|
|
|
continue;
|
|
|
|
last = sec;
|
2021-08-12 20:57:57 +00:00
|
|
|
textItems.push_back({ sec, hr });
|
2014-02-23 23:28:31 +00:00
|
|
|
last_printed_hr = hr;
|
|
|
|
}
|
|
|
|
setPolygon(poly);
|
|
|
|
|
2021-08-12 20:57:57 +00:00
|
|
|
for (size_t i = 0; i < textItems.size(); ++i) {
|
|
|
|
auto [sec, hr] = textItems[i];
|
|
|
|
createTextItem(sec, hr, i == textItems.size() - 1);
|
|
|
|
}
|
2014-02-23 23:28:31 +00:00
|
|
|
}
|
|
|
|
|
2021-08-12 20:57:57 +00:00
|
|
|
void DiveHeartrateItem::createTextItem(int sec, int hr, bool last)
|
2014-02-23 23:28:31 +00:00
|
|
|
{
|
2021-08-12 20:57:57 +00:00
|
|
|
int flags = last ? Qt::AlignLeft | Qt::AlignBottom :
|
|
|
|
Qt::AlignRight | Qt::AlignBottom;
|
|
|
|
DiveTextItem *text = new DiveTextItem(dpr, 0.7, flags, this);
|
2021-08-13 07:56:46 +00:00
|
|
|
text->set(QString("%1").arg(hr), getColor(HR_TEXT));
|
2020-12-20 17:12:55 +00:00
|
|
|
text->setPos(QPointF(hAxis.posAtValue(sec), vAxis.posAtValue(hr)));
|
2014-02-23 23:28:31 +00:00
|
|
|
texts.append(text);
|
|
|
|
}
|
|
|
|
|
2018-05-21 16:20:29 +00:00
|
|
|
void DiveHeartrateItem::paint(QPainter *painter, const QStyleOptionGraphicsItem*, QWidget*)
|
2014-02-23 23:28:31 +00:00
|
|
|
{
|
2014-02-28 04:09:57 +00:00
|
|
|
if (polygon().isEmpty())
|
2014-02-23 23:28:31 +00:00
|
|
|
return;
|
2014-07-17 23:18:14 +00:00
|
|
|
painter->save();
|
2014-02-23 23:28:31 +00:00
|
|
|
painter->setPen(pen());
|
|
|
|
painter->drawPolyline(polygon());
|
2014-07-17 23:18:14 +00:00
|
|
|
painter->restore();
|
2014-02-23 23:28:31 +00:00
|
|
|
}
|
|
|
|
|
2021-06-03 15:19:38 +00:00
|
|
|
DiveTemperatureItem::DiveTemperatureItem(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, int hColumn,
|
2021-08-09 14:48:08 +00:00
|
|
|
const DiveCartesianAxis &vAxis, int vColumn, double dpr) :
|
|
|
|
AbstractProfilePolygonItem(model, hAxis, hColumn, vAxis, vColumn, dpr)
|
2014-01-16 20:39:13 +00:00
|
|
|
{
|
|
|
|
QPen pen;
|
|
|
|
pen.setBrush(QBrush(getColor(::TEMP_PLOT)));
|
|
|
|
pen.setCosmetic(true);
|
|
|
|
pen.setWidth(2);
|
|
|
|
setPen(pen);
|
|
|
|
}
|
|
|
|
|
2021-10-08 19:00:37 +00:00
|
|
|
void DiveTemperatureItem::replot(const dive *, int fromIn, int toIn, bool)
|
2014-01-16 20:39:13 +00:00
|
|
|
{
|
2021-10-08 19:00:37 +00:00
|
|
|
from = fromIn;
|
|
|
|
to = toIn;
|
|
|
|
|
2021-10-09 12:56:12 +00:00
|
|
|
double last = -300.0, last_printed_temp = 0.0, last_valid_temp = 0.0, sec = 0.0;
|
2021-08-12 20:57:57 +00:00
|
|
|
std::vector<std::pair<int, int>> textItems;
|
2014-01-16 20:39:13 +00:00
|
|
|
|
2014-01-19 19:38:22 +00:00
|
|
|
qDeleteAll(texts);
|
|
|
|
texts.clear();
|
2014-01-16 20:39:13 +00:00
|
|
|
// Ignore empty values. things do not look good with '0' as temperature in kelvin...
|
|
|
|
QPolygonF poly;
|
2021-10-08 19:00:37 +00:00
|
|
|
for (int i = from; i < to; i++) {
|
2021-10-09 12:56:12 +00:00
|
|
|
auto [sec, mkelvin] = getPoint(i);
|
|
|
|
if (mkelvin < 1.0)
|
2014-01-16 20:39:13 +00:00
|
|
|
continue;
|
2020-12-20 17:12:55 +00:00
|
|
|
QPointF point(hAxis.posAtValue(sec), vAxis.posAtValue(mkelvin));
|
2014-01-16 20:39:13 +00:00
|
|
|
poly.append(point);
|
2021-10-09 12:56:12 +00:00
|
|
|
last_valid_temp = sec;
|
2014-01-17 19:54:47 +00:00
|
|
|
|
|
|
|
/* don't print a temperature
|
|
|
|
* if it's been less than 5min and less than a 2K change OR
|
|
|
|
* if it's been less than 2min OR if the change from the
|
|
|
|
* last print is less than .4K (and therefore less than 1F) */
|
2021-10-09 12:56:12 +00:00
|
|
|
if (((sec < last + 300.0) && (fabs(mkelvin - last_printed_temp) < 2000.0)) ||
|
|
|
|
(sec < last + 120.0) ||
|
|
|
|
(fabs(mkelvin - last_printed_temp) < 400.0))
|
2014-01-17 19:54:47 +00:00
|
|
|
continue;
|
|
|
|
last = sec;
|
2021-10-09 12:56:12 +00:00
|
|
|
if (mkelvin > 200000.0)
|
|
|
|
textItems.push_back({ static_cast<int>(sec), static_cast<int>(mkelvin) });
|
2014-01-17 19:54:47 +00:00
|
|
|
last_printed_temp = mkelvin;
|
2014-01-16 20:39:13 +00:00
|
|
|
}
|
|
|
|
setPolygon(poly);
|
2014-01-17 19:54:47 +00:00
|
|
|
|
2021-10-09 12:56:12 +00:00
|
|
|
/* print the end temperature, if it's different or if the
|
|
|
|
* last temperature print has been more than a quarter of the
|
|
|
|
* dive back */
|
|
|
|
if (last_valid_temp > 200000.0 &&
|
|
|
|
((fabs(last_valid_temp - last_printed_temp) > 500.0) || (last < 0.75 * sec))) {
|
|
|
|
textItems.push_back({ static_cast<int>(sec), static_cast<int>(last_valid_temp) });
|
2021-08-12 20:57:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < textItems.size(); ++i) {
|
|
|
|
auto [sec, mkelvin] = textItems[i];
|
|
|
|
createTextItem(sec, mkelvin, i == textItems.size() - 1);
|
2014-01-17 19:54:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-12 20:57:57 +00:00
|
|
|
void DiveTemperatureItem::createTextItem(int sec, int mkelvin, bool last)
|
2014-01-17 19:54:47 +00:00
|
|
|
{
|
2018-02-18 20:55:57 +00:00
|
|
|
temperature_t temp;
|
|
|
|
temp.mkelvin = mkelvin;
|
2014-01-19 19:38:22 +00:00
|
|
|
|
2021-08-12 20:57:57 +00:00
|
|
|
int flags = last ? Qt::AlignLeft | Qt::AlignBottom :
|
|
|
|
Qt::AlignRight | Qt::AlignBottom;
|
|
|
|
DiveTextItem *text = new DiveTextItem(dpr, 0.8, flags, this);
|
2021-08-13 07:56:46 +00:00
|
|
|
text->set(get_temperature_string(temp, true), getColor(TEMP_TEXT));
|
2020-12-20 17:12:55 +00:00
|
|
|
text->setPos(QPointF(hAxis.posAtValue(sec), vAxis.posAtValue(mkelvin)));
|
2014-01-23 18:07:22 +00:00
|
|
|
texts.append(text);
|
2014-01-16 20:39:13 +00:00
|
|
|
}
|
|
|
|
|
2018-05-21 16:20:29 +00:00
|
|
|
void DiveTemperatureItem::paint(QPainter *painter, const QStyleOptionGraphicsItem*, QWidget*)
|
2014-01-16 20:39:13 +00:00
|
|
|
{
|
2014-02-28 04:09:57 +00:00
|
|
|
if (polygon().isEmpty())
|
2014-02-10 16:41:59 +00:00
|
|
|
return;
|
2014-07-17 23:18:14 +00:00
|
|
|
painter->save();
|
2014-01-16 20:39:13 +00:00
|
|
|
painter->setPen(pen());
|
|
|
|
painter->drawPolyline(polygon());
|
2014-07-17 23:18:14 +00:00
|
|
|
painter->restore();
|
2014-01-16 20:39:13 +00:00
|
|
|
}
|
2014-01-17 17:34:15 +00:00
|
|
|
|
2021-06-03 15:19:38 +00:00
|
|
|
DiveMeanDepthItem::DiveMeanDepthItem(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, int hColumn,
|
2021-08-09 14:48:08 +00:00
|
|
|
const DiveCartesianAxis &vAxis, int vColumn, double dpr) :
|
|
|
|
AbstractProfilePolygonItem(model, hAxis, hColumn, vAxis, vColumn, dpr)
|
2015-01-01 23:28:38 +00:00
|
|
|
{
|
|
|
|
QPen pen;
|
2015-01-02 00:43:30 +00:00
|
|
|
pen.setBrush(QBrush(getColor(::HR_AXIS)));
|
2015-01-01 23:28:38 +00:00
|
|
|
pen.setCosmetic(true);
|
|
|
|
pen.setWidth(2);
|
|
|
|
setPen(pen);
|
2021-10-09 12:56:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Apparently, there can be samples without mean depth? If not, remove these functions.
|
|
|
|
std::pair<double,double> DiveMeanDepthItem::getMeanDepth(int i) const
|
|
|
|
{
|
|
|
|
for ( ; i >= 0; --i) {
|
|
|
|
const plot_data &entry = dataModel.data().entry[i];
|
|
|
|
if (entry.running_sum > 0)
|
|
|
|
return { static_cast<double>(entry.sec),
|
|
|
|
static_cast<double>(entry.running_sum) / entry.sec };
|
|
|
|
}
|
|
|
|
return { 0.0, 0.0 };
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<double,double> DiveMeanDepthItem::getNextMeanDepth(int first) const
|
|
|
|
{
|
|
|
|
int last = dataModel.data().nr;
|
|
|
|
for (int i = first + 1; i < last; ++i) {
|
|
|
|
const plot_data &entry = dataModel.data().entry[i];
|
|
|
|
if (entry.running_sum > 0)
|
|
|
|
return { static_cast<double>(entry.sec),
|
|
|
|
static_cast<double>(entry.running_sum) / entry.sec };
|
|
|
|
}
|
|
|
|
return getMeanDepth(first);
|
2015-01-01 23:28:38 +00:00
|
|
|
}
|
|
|
|
|
2021-10-08 19:00:37 +00:00
|
|
|
void DiveMeanDepthItem::replot(const dive *, int fromIn, int toIn, bool)
|
2015-01-01 23:28:38 +00:00
|
|
|
{
|
2021-10-08 19:00:37 +00:00
|
|
|
from = fromIn;
|
|
|
|
to = toIn;
|
|
|
|
|
2021-10-09 12:56:12 +00:00
|
|
|
double prevSec = 0.0, prevMeanDepth = 0.0;
|
2015-01-01 23:28:38 +00:00
|
|
|
|
|
|
|
QPolygonF poly;
|
2021-10-09 12:56:12 +00:00
|
|
|
for (int i = from; i < to; i++) {
|
|
|
|
auto [sec, meanDepth] = getMeanDepth(i);
|
2015-01-02 00:28:37 +00:00
|
|
|
// Ignore empty values
|
2021-10-09 12:56:12 +00:00
|
|
|
if (meanDepth == 0)
|
2015-01-02 00:28:37 +00:00
|
|
|
continue;
|
2021-10-09 12:56:12 +00:00
|
|
|
if (i == from && i < to) {
|
|
|
|
auto [sec2, meanDepth2] = getNextMeanDepth(i);
|
|
|
|
if (meanDepth2 > 0.0)
|
|
|
|
clipStart(sec, meanDepth, sec2, meanDepth2);
|
|
|
|
}
|
|
|
|
if (i == to - 1 && i > 0)
|
|
|
|
clipStop(sec, meanDepth, prevSec, prevMeanDepth);
|
|
|
|
|
2015-01-01 23:28:38 +00:00
|
|
|
|
2021-10-09 12:56:12 +00:00
|
|
|
QPointF point(hAxis.posAtValue(sec), vAxis.posAtValue(meanDepth));
|
2015-01-02 00:28:37 +00:00
|
|
|
poly.append(point);
|
2021-10-09 12:56:12 +00:00
|
|
|
|
|
|
|
prevSec = sec;
|
|
|
|
prevMeanDepth = meanDepth;
|
2015-01-01 23:28:38 +00:00
|
|
|
}
|
|
|
|
setPolygon(poly);
|
2021-10-09 12:56:12 +00:00
|
|
|
if (prevMeanDepth > 0.0)
|
|
|
|
createTextItem(prevSec, prevMeanDepth);
|
2015-01-01 23:28:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-21 16:20:29 +00:00
|
|
|
void DiveMeanDepthItem::paint(QPainter *painter, const QStyleOptionGraphicsItem*, QWidget*)
|
2015-01-01 23:28:38 +00:00
|
|
|
{
|
|
|
|
if (polygon().isEmpty())
|
|
|
|
return;
|
|
|
|
painter->save();
|
|
|
|
painter->setPen(pen());
|
|
|
|
painter->drawPolyline(polygon());
|
|
|
|
painter->restore();
|
|
|
|
}
|
2015-01-28 19:24:00 +00:00
|
|
|
|
2021-10-09 12:56:12 +00:00
|
|
|
void DiveMeanDepthItem::createTextItem(double lastSec, double lastMeanDepth)
|
2019-02-23 17:31:02 +00:00
|
|
|
{
|
2015-01-28 19:24:00 +00:00
|
|
|
qDeleteAll(texts);
|
|
|
|
texts.clear();
|
2021-08-12 20:57:57 +00:00
|
|
|
DiveTextItem *text = new DiveTextItem(dpr, 0.8, Qt::AlignRight | Qt::AlignTop, this);
|
2021-10-09 12:56:12 +00:00
|
|
|
text->set(get_depth_string(lrint(lastMeanDepth), true), getColor(TEMP_TEXT));
|
|
|
|
text->setPos(QPointF(hAxis.posAtValue(lastSec) + dpr, vAxis.posAtValue(lastMeanDepth)));
|
2015-01-28 19:24:00 +00:00
|
|
|
texts.append(text);
|
|
|
|
}
|
|
|
|
|
2021-10-08 19:00:37 +00:00
|
|
|
void DiveGasPressureItem::replot(const dive *d, int fromIn, int toIn, bool in_planner)
|
2014-01-17 17:34:15 +00:00
|
|
|
{
|
2021-10-08 19:00:37 +00:00
|
|
|
from = fromIn;
|
|
|
|
to = toIn;
|
|
|
|
|
2021-01-02 17:45:10 +00:00
|
|
|
const struct plot_info *pInfo = &dataModel.data();
|
2019-08-04 16:44:57 +00:00
|
|
|
std::vector<int> plotted_cyl(pInfo->nr_cylinders, false);
|
2021-10-09 12:56:12 +00:00
|
|
|
std::vector<double> last_plotted(pInfo->nr_cylinders, 0.0);
|
|
|
|
std::vector<Segment> act_segments(pInfo->nr_cylinders);
|
Profile support for multiple concurrent pressure sensors
This finally handles multiple cylinder pressures, both overlapping and
consecutive, and it seems to work on the nasty cases I've thrown at it.
Want to just track five different cylinders all at once, without any
pesky gas switch events? Sure, you can do that. It will show five
different gas pressures for your five cylinders, and they will go down
as you breathe down the cylinders.
I obviously don't have any real data for that case, but I do have a test
file with five actual cylinders that all have samples over the whole
course of the dive. The end result looks messy as hell, but what did
you expect?
HOWEVER.
The only way to do this sanely was
- actually make the "struct plot_info" have all the cylinder pressures
(so no "sensor index and pressure" - every cylinder has a pressure for
every plot info entry)
This obviously makes the plot_info much bigger. We used to have
MAX_CYLINDERS be a fairly generous 8, which seems sane. The planning
code made that 8 be 20. That seems questionable. But whatever.
The good news is that the plot-info should hopefully get freed, and
only be allocated one dive at a time, so the fact that it is big and
nasty shouldn't be a scaling issue, though.
- the "populate_pressure_information()" function had to be rewritten
quite a bit. The good news is that it's actually simpler now, although
I would not go so far as to really call it simple. It's still
complicated and suble, but now it explicitly just does one cylinder at
a time.
It *used* to have this insanely complicated "keep track of the pressure
ranges for every cylinder at once". I just couldn't stand that model
and keep my sanity, so it now just tracks one cylinder at a time, and
doesn't have an array of live data, instead the caller will just call
it for each cylinder.
- get rid of some of our hackier stuff, like the code that populates the
plot_info data code with the currently selected cylinder number, and
clears out any other pressures. That obviously does *not* work when you
may not have a single primary cylinder any more.
Now, the above sounds like all good things. Yeah, it mostly is.
BUT.
There's a few big downsides from the above:
- there's no sane way to do this as a series of small changes.
The change to make the plot_info take an array of cylinder pressures
rather than the sensor+pressure model really isn't amenable to "fix up
one use at a time". When you switch over to the new data structure
model, you have to switch over to the new way of populating the
pressure ranges. The two just go hand in hand.
- Some of our code *depended* on the "sensor+pressure" model. I fixed all
the ones I could sanely fix. There was one particular case that I just
couldn't sanely fix, and I didn't care enough about it to do something
insane.
So the only _known_ breakage is the "TankItem" profile widget. That's
the bar at the bottom of the profile that shows which cylinder is in
use right now. You'd think that would be trivial to fix up, and yes it
would be - I could just use the regular model of
firstcyl = explicit_first_cylinder(dive, dc)
.. then iterate over the gas change events to see the others ..
but the problem with the "TankItem" widget is that it does its own
model, and it has thrown away the dive and the dive computer
information. It just doesn't even know. It only knows what cylinders
there are, and the plot_info. And it just used to look at the sensor
number in the plot_info, and be done with that. That number no longer
exists.
- I have tested it, and I think the code is better, but hey, it's a
fairly large patch to some of the more complex code in our code base.
That "interpolate missing pressure fields" code really isn't pretty. It
may be prettier, but..
Anyway, without further ado, here's the patch. No sign-off yet, because I
do think people should look and comment. But I think the patch is fine,
and I'll fix anythign that anybody can find, *except* for that TankItem
thing that I will refuse to touch. That class is ugly. It needs to have
access to the actual dive.
Note how it actually does remove more lines than it adds, and that's
despite added comments etc. The code really is simpler, but there may be
cases in there that need more work.
Known missing pieces that don't currently take advantage of concurrent
cylinder pressure data:
- the momentary SAC rate coloring for dives will need more work
- dive merging (but we expect to generally normally not merge dive
computers, which is the main source of sensor data)
- actually taking advantage of different sensor data from different
dive computers
But most of all: Testing. Lots and lots of testing to find all the
corner cases.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-27 17:17:05 +00:00
|
|
|
QPolygonF boundingPoly;
|
2021-10-09 12:56:12 +00:00
|
|
|
segments.clear();
|
2014-01-17 17:34:15 +00:00
|
|
|
|
2021-10-08 19:00:37 +00:00
|
|
|
for (int i = from; i < to; i++) {
|
2019-07-06 09:58:27 +00:00
|
|
|
const struct plot_data *entry = pInfo->entry + i;
|
2014-01-21 15:35:40 +00:00
|
|
|
|
2019-08-04 16:44:57 +00:00
|
|
|
for (int cyl = 0; cyl < pInfo->nr_cylinders; cyl++) {
|
2021-10-09 12:56:12 +00:00
|
|
|
double mbar = static_cast<double>(get_plot_pressure(pInfo, i, cyl));
|
|
|
|
double time = static_cast<double>(entry->sec);
|
Profile support for multiple concurrent pressure sensors
This finally handles multiple cylinder pressures, both overlapping and
consecutive, and it seems to work on the nasty cases I've thrown at it.
Want to just track five different cylinders all at once, without any
pesky gas switch events? Sure, you can do that. It will show five
different gas pressures for your five cylinders, and they will go down
as you breathe down the cylinders.
I obviously don't have any real data for that case, but I do have a test
file with five actual cylinders that all have samples over the whole
course of the dive. The end result looks messy as hell, but what did
you expect?
HOWEVER.
The only way to do this sanely was
- actually make the "struct plot_info" have all the cylinder pressures
(so no "sensor index and pressure" - every cylinder has a pressure for
every plot info entry)
This obviously makes the plot_info much bigger. We used to have
MAX_CYLINDERS be a fairly generous 8, which seems sane. The planning
code made that 8 be 20. That seems questionable. But whatever.
The good news is that the plot-info should hopefully get freed, and
only be allocated one dive at a time, so the fact that it is big and
nasty shouldn't be a scaling issue, though.
- the "populate_pressure_information()" function had to be rewritten
quite a bit. The good news is that it's actually simpler now, although
I would not go so far as to really call it simple. It's still
complicated and suble, but now it explicitly just does one cylinder at
a time.
It *used* to have this insanely complicated "keep track of the pressure
ranges for every cylinder at once". I just couldn't stand that model
and keep my sanity, so it now just tracks one cylinder at a time, and
doesn't have an array of live data, instead the caller will just call
it for each cylinder.
- get rid of some of our hackier stuff, like the code that populates the
plot_info data code with the currently selected cylinder number, and
clears out any other pressures. That obviously does *not* work when you
may not have a single primary cylinder any more.
Now, the above sounds like all good things. Yeah, it mostly is.
BUT.
There's a few big downsides from the above:
- there's no sane way to do this as a series of small changes.
The change to make the plot_info take an array of cylinder pressures
rather than the sensor+pressure model really isn't amenable to "fix up
one use at a time". When you switch over to the new data structure
model, you have to switch over to the new way of populating the
pressure ranges. The two just go hand in hand.
- Some of our code *depended* on the "sensor+pressure" model. I fixed all
the ones I could sanely fix. There was one particular case that I just
couldn't sanely fix, and I didn't care enough about it to do something
insane.
So the only _known_ breakage is the "TankItem" profile widget. That's
the bar at the bottom of the profile that shows which cylinder is in
use right now. You'd think that would be trivial to fix up, and yes it
would be - I could just use the regular model of
firstcyl = explicit_first_cylinder(dive, dc)
.. then iterate over the gas change events to see the others ..
but the problem with the "TankItem" widget is that it does its own
model, and it has thrown away the dive and the dive computer
information. It just doesn't even know. It only knows what cylinders
there are, and the plot_info. And it just used to look at the sensor
number in the plot_info, and be done with that. That number no longer
exists.
- I have tested it, and I think the code is better, but hey, it's a
fairly large patch to some of the more complex code in our code base.
That "interpolate missing pressure fields" code really isn't pretty. It
may be prettier, but..
Anyway, without further ado, here's the patch. No sign-off yet, because I
do think people should look and comment. But I think the patch is fine,
and I'll fix anythign that anybody can find, *except* for that TankItem
thing that I will refuse to touch. That class is ugly. It needs to have
access to the actual dive.
Note how it actually does remove more lines than it adds, and that's
despite added comments etc. The code really is simpler, but there may be
cases in there that need more work.
Known missing pieces that don't currently take advantage of concurrent
cylinder pressure data:
- the momentary SAC rate coloring for dives will need more work
- dive merging (but we expect to generally normally not merge dive
computers, which is the main source of sensor data)
- actually taking advantage of different sensor data from different
dive computers
But most of all: Testing. Lots and lots of testing to find all the
corner cases.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-27 17:17:05 +00:00
|
|
|
|
2021-10-09 12:56:12 +00:00
|
|
|
if (mbar < 1.0)
|
Profile support for multiple concurrent pressure sensors
This finally handles multiple cylinder pressures, both overlapping and
consecutive, and it seems to work on the nasty cases I've thrown at it.
Want to just track five different cylinders all at once, without any
pesky gas switch events? Sure, you can do that. It will show five
different gas pressures for your five cylinders, and they will go down
as you breathe down the cylinders.
I obviously don't have any real data for that case, but I do have a test
file with five actual cylinders that all have samples over the whole
course of the dive. The end result looks messy as hell, but what did
you expect?
HOWEVER.
The only way to do this sanely was
- actually make the "struct plot_info" have all the cylinder pressures
(so no "sensor index and pressure" - every cylinder has a pressure for
every plot info entry)
This obviously makes the plot_info much bigger. We used to have
MAX_CYLINDERS be a fairly generous 8, which seems sane. The planning
code made that 8 be 20. That seems questionable. But whatever.
The good news is that the plot-info should hopefully get freed, and
only be allocated one dive at a time, so the fact that it is big and
nasty shouldn't be a scaling issue, though.
- the "populate_pressure_information()" function had to be rewritten
quite a bit. The good news is that it's actually simpler now, although
I would not go so far as to really call it simple. It's still
complicated and suble, but now it explicitly just does one cylinder at
a time.
It *used* to have this insanely complicated "keep track of the pressure
ranges for every cylinder at once". I just couldn't stand that model
and keep my sanity, so it now just tracks one cylinder at a time, and
doesn't have an array of live data, instead the caller will just call
it for each cylinder.
- get rid of some of our hackier stuff, like the code that populates the
plot_info data code with the currently selected cylinder number, and
clears out any other pressures. That obviously does *not* work when you
may not have a single primary cylinder any more.
Now, the above sounds like all good things. Yeah, it mostly is.
BUT.
There's a few big downsides from the above:
- there's no sane way to do this as a series of small changes.
The change to make the plot_info take an array of cylinder pressures
rather than the sensor+pressure model really isn't amenable to "fix up
one use at a time". When you switch over to the new data structure
model, you have to switch over to the new way of populating the
pressure ranges. The two just go hand in hand.
- Some of our code *depended* on the "sensor+pressure" model. I fixed all
the ones I could sanely fix. There was one particular case that I just
couldn't sanely fix, and I didn't care enough about it to do something
insane.
So the only _known_ breakage is the "TankItem" profile widget. That's
the bar at the bottom of the profile that shows which cylinder is in
use right now. You'd think that would be trivial to fix up, and yes it
would be - I could just use the regular model of
firstcyl = explicit_first_cylinder(dive, dc)
.. then iterate over the gas change events to see the others ..
but the problem with the "TankItem" widget is that it does its own
model, and it has thrown away the dive and the dive computer
information. It just doesn't even know. It only knows what cylinders
there are, and the plot_info. And it just used to look at the sensor
number in the plot_info, and be done with that. That number no longer
exists.
- I have tested it, and I think the code is better, but hey, it's a
fairly large patch to some of the more complex code in our code base.
That "interpolate missing pressure fields" code really isn't pretty. It
may be prettier, but..
Anyway, without further ado, here's the patch. No sign-off yet, because I
do think people should look and comment. But I think the patch is fine,
and I'll fix anythign that anybody can find, *except* for that TankItem
thing that I will refuse to touch. That class is ugly. It needs to have
access to the actual dive.
Note how it actually does remove more lines than it adds, and that's
despite added comments etc. The code really is simpler, but there may be
cases in there that need more work.
Known missing pieces that don't currently take advantage of concurrent
cylinder pressure data:
- the momentary SAC rate coloring for dives will need more work
- dive merging (but we expect to generally normally not merge dive
computers, which is the main source of sensor data)
- actually taking advantage of different sensor data from different
dive computers
But most of all: Testing. Lots and lots of testing to find all the
corner cases.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-27 17:17:05 +00:00
|
|
|
continue;
|
|
|
|
|
2021-10-09 12:56:12 +00:00
|
|
|
if (i == from && i < to - 1) {
|
|
|
|
double mbar2 = static_cast<double>(get_plot_pressure(pInfo, i+1, cyl));
|
|
|
|
double time2 = static_cast<double>(entry[1].sec);
|
|
|
|
if (mbar2 < 1.0)
|
|
|
|
continue;
|
|
|
|
clipStart(time, mbar, time2, mbar2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == to - 1 && i > from) {
|
|
|
|
double mbar2 = static_cast<double>(get_plot_pressure(pInfo, i-1, cyl));
|
|
|
|
double time2 = static_cast<double>(entry[-1].sec);
|
|
|
|
if (mbar2 < 1.0)
|
|
|
|
continue;
|
|
|
|
clipStop(time, mbar, time2, mbar2);
|
|
|
|
}
|
|
|
|
|
2020-12-20 17:12:55 +00:00
|
|
|
QPointF point(hAxis.posAtValue(time), vAxis.posAtValue(mbar));
|
Profile support for multiple concurrent pressure sensors
This finally handles multiple cylinder pressures, both overlapping and
consecutive, and it seems to work on the nasty cases I've thrown at it.
Want to just track five different cylinders all at once, without any
pesky gas switch events? Sure, you can do that. It will show five
different gas pressures for your five cylinders, and they will go down
as you breathe down the cylinders.
I obviously don't have any real data for that case, but I do have a test
file with five actual cylinders that all have samples over the whole
course of the dive. The end result looks messy as hell, but what did
you expect?
HOWEVER.
The only way to do this sanely was
- actually make the "struct plot_info" have all the cylinder pressures
(so no "sensor index and pressure" - every cylinder has a pressure for
every plot info entry)
This obviously makes the plot_info much bigger. We used to have
MAX_CYLINDERS be a fairly generous 8, which seems sane. The planning
code made that 8 be 20. That seems questionable. But whatever.
The good news is that the plot-info should hopefully get freed, and
only be allocated one dive at a time, so the fact that it is big and
nasty shouldn't be a scaling issue, though.
- the "populate_pressure_information()" function had to be rewritten
quite a bit. The good news is that it's actually simpler now, although
I would not go so far as to really call it simple. It's still
complicated and suble, but now it explicitly just does one cylinder at
a time.
It *used* to have this insanely complicated "keep track of the pressure
ranges for every cylinder at once". I just couldn't stand that model
and keep my sanity, so it now just tracks one cylinder at a time, and
doesn't have an array of live data, instead the caller will just call
it for each cylinder.
- get rid of some of our hackier stuff, like the code that populates the
plot_info data code with the currently selected cylinder number, and
clears out any other pressures. That obviously does *not* work when you
may not have a single primary cylinder any more.
Now, the above sounds like all good things. Yeah, it mostly is.
BUT.
There's a few big downsides from the above:
- there's no sane way to do this as a series of small changes.
The change to make the plot_info take an array of cylinder pressures
rather than the sensor+pressure model really isn't amenable to "fix up
one use at a time". When you switch over to the new data structure
model, you have to switch over to the new way of populating the
pressure ranges. The two just go hand in hand.
- Some of our code *depended* on the "sensor+pressure" model. I fixed all
the ones I could sanely fix. There was one particular case that I just
couldn't sanely fix, and I didn't care enough about it to do something
insane.
So the only _known_ breakage is the "TankItem" profile widget. That's
the bar at the bottom of the profile that shows which cylinder is in
use right now. You'd think that would be trivial to fix up, and yes it
would be - I could just use the regular model of
firstcyl = explicit_first_cylinder(dive, dc)
.. then iterate over the gas change events to see the others ..
but the problem with the "TankItem" widget is that it does its own
model, and it has thrown away the dive and the dive computer
information. It just doesn't even know. It only knows what cylinders
there are, and the plot_info. And it just used to look at the sensor
number in the plot_info, and be done with that. That number no longer
exists.
- I have tested it, and I think the code is better, but hey, it's a
fairly large patch to some of the more complex code in our code base.
That "interpolate missing pressure fields" code really isn't pretty. It
may be prettier, but..
Anyway, without further ado, here's the patch. No sign-off yet, because I
do think people should look and comment. But I think the patch is fine,
and I'll fix anythign that anybody can find, *except* for that TankItem
thing that I will refuse to touch. That class is ugly. It needs to have
access to the actual dive.
Note how it actually does remove more lines than it adds, and that's
despite added comments etc. The code really is simpler, but there may be
cases in there that need more work.
Known missing pieces that don't currently take advantage of concurrent
cylinder pressure data:
- the momentary SAC rate coloring for dives will need more work
- dive merging (but we expect to generally normally not merge dive
computers, which is the main source of sensor data)
- actually taking advantage of different sensor data from different
dive computers
But most of all: Testing. Lots and lots of testing to find all the
corner cases.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-27 17:17:05 +00:00
|
|
|
boundingPoly.push_back(point);
|
|
|
|
|
2021-01-03 23:00:38 +00:00
|
|
|
QColor color;
|
2021-03-29 18:47:48 +00:00
|
|
|
if (!in_planner) {
|
2021-01-03 23:00:38 +00:00
|
|
|
if (entry->sac)
|
2021-01-09 16:36:48 +00:00
|
|
|
color = getSacColor(entry->sac, d->sac);
|
2021-01-03 23:00:38 +00:00
|
|
|
else
|
|
|
|
color = MED_GRAY_HIGH_TRANS;
|
|
|
|
} else {
|
2021-10-09 12:56:12 +00:00
|
|
|
if (mbar < 0.0)
|
2021-01-03 23:00:38 +00:00
|
|
|
color = MAGENTA;
|
|
|
|
else
|
|
|
|
color = getPressureColor(entry->density);
|
|
|
|
}
|
|
|
|
|
2021-10-09 12:56:12 +00:00
|
|
|
if (!act_segments[cyl].polygon.empty()) {
|
2021-01-03 23:00:38 +00:00
|
|
|
/* Have we used this cylinder in the last two minutes? Continue */
|
2021-10-09 12:56:12 +00:00
|
|
|
if (time - act_segments[cyl].last.time <= 2*60) {
|
|
|
|
act_segments[cyl].polygon.push_back({ point, color });
|
|
|
|
act_segments[cyl].last.time = time;
|
|
|
|
act_segments[cyl].last.pressure = mbar;
|
Profile support for multiple concurrent pressure sensors
This finally handles multiple cylinder pressures, both overlapping and
consecutive, and it seems to work on the nasty cases I've thrown at it.
Want to just track five different cylinders all at once, without any
pesky gas switch events? Sure, you can do that. It will show five
different gas pressures for your five cylinders, and they will go down
as you breathe down the cylinders.
I obviously don't have any real data for that case, but I do have a test
file with five actual cylinders that all have samples over the whole
course of the dive. The end result looks messy as hell, but what did
you expect?
HOWEVER.
The only way to do this sanely was
- actually make the "struct plot_info" have all the cylinder pressures
(so no "sensor index and pressure" - every cylinder has a pressure for
every plot info entry)
This obviously makes the plot_info much bigger. We used to have
MAX_CYLINDERS be a fairly generous 8, which seems sane. The planning
code made that 8 be 20. That seems questionable. But whatever.
The good news is that the plot-info should hopefully get freed, and
only be allocated one dive at a time, so the fact that it is big and
nasty shouldn't be a scaling issue, though.
- the "populate_pressure_information()" function had to be rewritten
quite a bit. The good news is that it's actually simpler now, although
I would not go so far as to really call it simple. It's still
complicated and suble, but now it explicitly just does one cylinder at
a time.
It *used* to have this insanely complicated "keep track of the pressure
ranges for every cylinder at once". I just couldn't stand that model
and keep my sanity, so it now just tracks one cylinder at a time, and
doesn't have an array of live data, instead the caller will just call
it for each cylinder.
- get rid of some of our hackier stuff, like the code that populates the
plot_info data code with the currently selected cylinder number, and
clears out any other pressures. That obviously does *not* work when you
may not have a single primary cylinder any more.
Now, the above sounds like all good things. Yeah, it mostly is.
BUT.
There's a few big downsides from the above:
- there's no sane way to do this as a series of small changes.
The change to make the plot_info take an array of cylinder pressures
rather than the sensor+pressure model really isn't amenable to "fix up
one use at a time". When you switch over to the new data structure
model, you have to switch over to the new way of populating the
pressure ranges. The two just go hand in hand.
- Some of our code *depended* on the "sensor+pressure" model. I fixed all
the ones I could sanely fix. There was one particular case that I just
couldn't sanely fix, and I didn't care enough about it to do something
insane.
So the only _known_ breakage is the "TankItem" profile widget. That's
the bar at the bottom of the profile that shows which cylinder is in
use right now. You'd think that would be trivial to fix up, and yes it
would be - I could just use the regular model of
firstcyl = explicit_first_cylinder(dive, dc)
.. then iterate over the gas change events to see the others ..
but the problem with the "TankItem" widget is that it does its own
model, and it has thrown away the dive and the dive computer
information. It just doesn't even know. It only knows what cylinders
there are, and the plot_info. And it just used to look at the sensor
number in the plot_info, and be done with that. That number no longer
exists.
- I have tested it, and I think the code is better, but hey, it's a
fairly large patch to some of the more complex code in our code base.
That "interpolate missing pressure fields" code really isn't pretty. It
may be prettier, but..
Anyway, without further ado, here's the patch. No sign-off yet, because I
do think people should look and comment. But I think the patch is fine,
and I'll fix anythign that anybody can find, *except* for that TankItem
thing that I will refuse to touch. That class is ugly. It needs to have
access to the actual dive.
Note how it actually does remove more lines than it adds, and that's
despite added comments etc. The code really is simpler, but there may be
cases in there that need more work.
Known missing pieces that don't currently take advantage of concurrent
cylinder pressure data:
- the momentary SAC rate coloring for dives will need more work
- dive merging (but we expect to generally normally not merge dive
computers, which is the main source of sensor data)
- actually taking advantage of different sensor data from different
dive computers
But most of all: Testing. Lots and lots of testing to find all the
corner cases.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-27 17:17:05 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Finish the previous one, start a new one */
|
2021-10-09 12:56:12 +00:00
|
|
|
act_segments[cyl].cyl = cyl;
|
|
|
|
segments.push_back(std::move(act_segments[cyl]));
|
|
|
|
act_segments[cyl] = Segment();
|
Profile support for multiple concurrent pressure sensors
This finally handles multiple cylinder pressures, both overlapping and
consecutive, and it seems to work on the nasty cases I've thrown at it.
Want to just track five different cylinders all at once, without any
pesky gas switch events? Sure, you can do that. It will show five
different gas pressures for your five cylinders, and they will go down
as you breathe down the cylinders.
I obviously don't have any real data for that case, but I do have a test
file with five actual cylinders that all have samples over the whole
course of the dive. The end result looks messy as hell, but what did
you expect?
HOWEVER.
The only way to do this sanely was
- actually make the "struct plot_info" have all the cylinder pressures
(so no "sensor index and pressure" - every cylinder has a pressure for
every plot info entry)
This obviously makes the plot_info much bigger. We used to have
MAX_CYLINDERS be a fairly generous 8, which seems sane. The planning
code made that 8 be 20. That seems questionable. But whatever.
The good news is that the plot-info should hopefully get freed, and
only be allocated one dive at a time, so the fact that it is big and
nasty shouldn't be a scaling issue, though.
- the "populate_pressure_information()" function had to be rewritten
quite a bit. The good news is that it's actually simpler now, although
I would not go so far as to really call it simple. It's still
complicated and suble, but now it explicitly just does one cylinder at
a time.
It *used* to have this insanely complicated "keep track of the pressure
ranges for every cylinder at once". I just couldn't stand that model
and keep my sanity, so it now just tracks one cylinder at a time, and
doesn't have an array of live data, instead the caller will just call
it for each cylinder.
- get rid of some of our hackier stuff, like the code that populates the
plot_info data code with the currently selected cylinder number, and
clears out any other pressures. That obviously does *not* work when you
may not have a single primary cylinder any more.
Now, the above sounds like all good things. Yeah, it mostly is.
BUT.
There's a few big downsides from the above:
- there's no sane way to do this as a series of small changes.
The change to make the plot_info take an array of cylinder pressures
rather than the sensor+pressure model really isn't amenable to "fix up
one use at a time". When you switch over to the new data structure
model, you have to switch over to the new way of populating the
pressure ranges. The two just go hand in hand.
- Some of our code *depended* on the "sensor+pressure" model. I fixed all
the ones I could sanely fix. There was one particular case that I just
couldn't sanely fix, and I didn't care enough about it to do something
insane.
So the only _known_ breakage is the "TankItem" profile widget. That's
the bar at the bottom of the profile that shows which cylinder is in
use right now. You'd think that would be trivial to fix up, and yes it
would be - I could just use the regular model of
firstcyl = explicit_first_cylinder(dive, dc)
.. then iterate over the gas change events to see the others ..
but the problem with the "TankItem" widget is that it does its own
model, and it has thrown away the dive and the dive computer
information. It just doesn't even know. It only knows what cylinders
there are, and the plot_info. And it just used to look at the sensor
number in the plot_info, and be done with that. That number no longer
exists.
- I have tested it, and I think the code is better, but hey, it's a
fairly large patch to some of the more complex code in our code base.
That "interpolate missing pressure fields" code really isn't pretty. It
may be prettier, but..
Anyway, without further ado, here's the patch. No sign-off yet, because I
do think people should look and comment. But I think the patch is fine,
and I'll fix anythign that anybody can find, *except* for that TankItem
thing that I will refuse to touch. That class is ugly. It needs to have
access to the actual dive.
Note how it actually does remove more lines than it adds, and that's
despite added comments etc. The code really is simpler, but there may be
cases in there that need more work.
Known missing pieces that don't currently take advantage of concurrent
cylinder pressure data:
- the momentary SAC rate coloring for dives will need more work
- dive merging (but we expect to generally normally not merge dive
computers, which is the main source of sensor data)
- actually taking advantage of different sensor data from different
dive computers
But most of all: Testing. Lots and lots of testing to find all the
corner cases.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-27 17:17:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
plotted_cyl[cyl] = true;
|
2021-10-09 12:56:12 +00:00
|
|
|
act_segments[cyl].polygon.push_back({ point, color });
|
|
|
|
act_segments[cyl].last.time = time;
|
|
|
|
act_segments[cyl].last.pressure = mbar;
|
|
|
|
if (act_segments[cyl].first.pressure == 0.0) {
|
|
|
|
act_segments[cyl].first.time = time;
|
|
|
|
act_segments[cyl].first.pressure = mbar;
|
|
|
|
}
|
2014-11-17 16:39:40 +00:00
|
|
|
}
|
Profile support for multiple concurrent pressure sensors
This finally handles multiple cylinder pressures, both overlapping and
consecutive, and it seems to work on the nasty cases I've thrown at it.
Want to just track five different cylinders all at once, without any
pesky gas switch events? Sure, you can do that. It will show five
different gas pressures for your five cylinders, and they will go down
as you breathe down the cylinders.
I obviously don't have any real data for that case, but I do have a test
file with five actual cylinders that all have samples over the whole
course of the dive. The end result looks messy as hell, but what did
you expect?
HOWEVER.
The only way to do this sanely was
- actually make the "struct plot_info" have all the cylinder pressures
(so no "sensor index and pressure" - every cylinder has a pressure for
every plot info entry)
This obviously makes the plot_info much bigger. We used to have
MAX_CYLINDERS be a fairly generous 8, which seems sane. The planning
code made that 8 be 20. That seems questionable. But whatever.
The good news is that the plot-info should hopefully get freed, and
only be allocated one dive at a time, so the fact that it is big and
nasty shouldn't be a scaling issue, though.
- the "populate_pressure_information()" function had to be rewritten
quite a bit. The good news is that it's actually simpler now, although
I would not go so far as to really call it simple. It's still
complicated and suble, but now it explicitly just does one cylinder at
a time.
It *used* to have this insanely complicated "keep track of the pressure
ranges for every cylinder at once". I just couldn't stand that model
and keep my sanity, so it now just tracks one cylinder at a time, and
doesn't have an array of live data, instead the caller will just call
it for each cylinder.
- get rid of some of our hackier stuff, like the code that populates the
plot_info data code with the currently selected cylinder number, and
clears out any other pressures. That obviously does *not* work when you
may not have a single primary cylinder any more.
Now, the above sounds like all good things. Yeah, it mostly is.
BUT.
There's a few big downsides from the above:
- there's no sane way to do this as a series of small changes.
The change to make the plot_info take an array of cylinder pressures
rather than the sensor+pressure model really isn't amenable to "fix up
one use at a time". When you switch over to the new data structure
model, you have to switch over to the new way of populating the
pressure ranges. The two just go hand in hand.
- Some of our code *depended* on the "sensor+pressure" model. I fixed all
the ones I could sanely fix. There was one particular case that I just
couldn't sanely fix, and I didn't care enough about it to do something
insane.
So the only _known_ breakage is the "TankItem" profile widget. That's
the bar at the bottom of the profile that shows which cylinder is in
use right now. You'd think that would be trivial to fix up, and yes it
would be - I could just use the regular model of
firstcyl = explicit_first_cylinder(dive, dc)
.. then iterate over the gas change events to see the others ..
but the problem with the "TankItem" widget is that it does its own
model, and it has thrown away the dive and the dive computer
information. It just doesn't even know. It only knows what cylinders
there are, and the plot_info. And it just used to look at the sensor
number in the plot_info, and be done with that. That number no longer
exists.
- I have tested it, and I think the code is better, but hey, it's a
fairly large patch to some of the more complex code in our code base.
That "interpolate missing pressure fields" code really isn't pretty. It
may be prettier, but..
Anyway, without further ado, here's the patch. No sign-off yet, because I
do think people should look and comment. But I think the patch is fine,
and I'll fix anythign that anybody can find, *except* for that TankItem
thing that I will refuse to touch. That class is ugly. It needs to have
access to the actual dive.
Note how it actually does remove more lines than it adds, and that's
despite added comments etc. The code really is simpler, but there may be
cases in there that need more work.
Known missing pieces that don't currently take advantage of concurrent
cylinder pressure data:
- the momentary SAC rate coloring for dives will need more work
- dive merging (but we expect to generally normally not merge dive
computers, which is the main source of sensor data)
- actually taking advantage of different sensor data from different
dive computers
But most of all: Testing. Lots and lots of testing to find all the
corner cases.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-27 17:17:05 +00:00
|
|
|
}
|
2014-01-17 17:34:15 +00:00
|
|
|
|
2019-08-04 16:44:57 +00:00
|
|
|
for (int cyl = 0; cyl < pInfo->nr_cylinders; cyl++) {
|
2021-10-09 12:56:12 +00:00
|
|
|
if (act_segments[cyl].polygon.empty())
|
Profile support for multiple concurrent pressure sensors
This finally handles multiple cylinder pressures, both overlapping and
consecutive, and it seems to work on the nasty cases I've thrown at it.
Want to just track five different cylinders all at once, without any
pesky gas switch events? Sure, you can do that. It will show five
different gas pressures for your five cylinders, and they will go down
as you breathe down the cylinders.
I obviously don't have any real data for that case, but I do have a test
file with five actual cylinders that all have samples over the whole
course of the dive. The end result looks messy as hell, but what did
you expect?
HOWEVER.
The only way to do this sanely was
- actually make the "struct plot_info" have all the cylinder pressures
(so no "sensor index and pressure" - every cylinder has a pressure for
every plot info entry)
This obviously makes the plot_info much bigger. We used to have
MAX_CYLINDERS be a fairly generous 8, which seems sane. The planning
code made that 8 be 20. That seems questionable. But whatever.
The good news is that the plot-info should hopefully get freed, and
only be allocated one dive at a time, so the fact that it is big and
nasty shouldn't be a scaling issue, though.
- the "populate_pressure_information()" function had to be rewritten
quite a bit. The good news is that it's actually simpler now, although
I would not go so far as to really call it simple. It's still
complicated and suble, but now it explicitly just does one cylinder at
a time.
It *used* to have this insanely complicated "keep track of the pressure
ranges for every cylinder at once". I just couldn't stand that model
and keep my sanity, so it now just tracks one cylinder at a time, and
doesn't have an array of live data, instead the caller will just call
it for each cylinder.
- get rid of some of our hackier stuff, like the code that populates the
plot_info data code with the currently selected cylinder number, and
clears out any other pressures. That obviously does *not* work when you
may not have a single primary cylinder any more.
Now, the above sounds like all good things. Yeah, it mostly is.
BUT.
There's a few big downsides from the above:
- there's no sane way to do this as a series of small changes.
The change to make the plot_info take an array of cylinder pressures
rather than the sensor+pressure model really isn't amenable to "fix up
one use at a time". When you switch over to the new data structure
model, you have to switch over to the new way of populating the
pressure ranges. The two just go hand in hand.
- Some of our code *depended* on the "sensor+pressure" model. I fixed all
the ones I could sanely fix. There was one particular case that I just
couldn't sanely fix, and I didn't care enough about it to do something
insane.
So the only _known_ breakage is the "TankItem" profile widget. That's
the bar at the bottom of the profile that shows which cylinder is in
use right now. You'd think that would be trivial to fix up, and yes it
would be - I could just use the regular model of
firstcyl = explicit_first_cylinder(dive, dc)
.. then iterate over the gas change events to see the others ..
but the problem with the "TankItem" widget is that it does its own
model, and it has thrown away the dive and the dive computer
information. It just doesn't even know. It only knows what cylinders
there are, and the plot_info. And it just used to look at the sensor
number in the plot_info, and be done with that. That number no longer
exists.
- I have tested it, and I think the code is better, but hey, it's a
fairly large patch to some of the more complex code in our code base.
That "interpolate missing pressure fields" code really isn't pretty. It
may be prettier, but..
Anyway, without further ado, here's the patch. No sign-off yet, because I
do think people should look and comment. But I think the patch is fine,
and I'll fix anythign that anybody can find, *except* for that TankItem
thing that I will refuse to touch. That class is ugly. It needs to have
access to the actual dive.
Note how it actually does remove more lines than it adds, and that's
despite added comments etc. The code really is simpler, but there may be
cases in there that need more work.
Known missing pieces that don't currently take advantage of concurrent
cylinder pressure data:
- the momentary SAC rate coloring for dives will need more work
- dive merging (but we expect to generally normally not merge dive
computers, which is the main source of sensor data)
- actually taking advantage of different sensor data from different
dive computers
But most of all: Testing. Lots and lots of testing to find all the
corner cases.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-27 17:17:05 +00:00
|
|
|
continue;
|
2021-10-09 12:56:12 +00:00
|
|
|
act_segments[cyl].cyl = cyl;
|
|
|
|
segments.push_back(std::move(act_segments[cyl]));
|
2014-01-17 17:34:15 +00:00
|
|
|
}
|
Profile support for multiple concurrent pressure sensors
This finally handles multiple cylinder pressures, both overlapping and
consecutive, and it seems to work on the nasty cases I've thrown at it.
Want to just track five different cylinders all at once, without any
pesky gas switch events? Sure, you can do that. It will show five
different gas pressures for your five cylinders, and they will go down
as you breathe down the cylinders.
I obviously don't have any real data for that case, but I do have a test
file with five actual cylinders that all have samples over the whole
course of the dive. The end result looks messy as hell, but what did
you expect?
HOWEVER.
The only way to do this sanely was
- actually make the "struct plot_info" have all the cylinder pressures
(so no "sensor index and pressure" - every cylinder has a pressure for
every plot info entry)
This obviously makes the plot_info much bigger. We used to have
MAX_CYLINDERS be a fairly generous 8, which seems sane. The planning
code made that 8 be 20. That seems questionable. But whatever.
The good news is that the plot-info should hopefully get freed, and
only be allocated one dive at a time, so the fact that it is big and
nasty shouldn't be a scaling issue, though.
- the "populate_pressure_information()" function had to be rewritten
quite a bit. The good news is that it's actually simpler now, although
I would not go so far as to really call it simple. It's still
complicated and suble, but now it explicitly just does one cylinder at
a time.
It *used* to have this insanely complicated "keep track of the pressure
ranges for every cylinder at once". I just couldn't stand that model
and keep my sanity, so it now just tracks one cylinder at a time, and
doesn't have an array of live data, instead the caller will just call
it for each cylinder.
- get rid of some of our hackier stuff, like the code that populates the
plot_info data code with the currently selected cylinder number, and
clears out any other pressures. That obviously does *not* work when you
may not have a single primary cylinder any more.
Now, the above sounds like all good things. Yeah, it mostly is.
BUT.
There's a few big downsides from the above:
- there's no sane way to do this as a series of small changes.
The change to make the plot_info take an array of cylinder pressures
rather than the sensor+pressure model really isn't amenable to "fix up
one use at a time". When you switch over to the new data structure
model, you have to switch over to the new way of populating the
pressure ranges. The two just go hand in hand.
- Some of our code *depended* on the "sensor+pressure" model. I fixed all
the ones I could sanely fix. There was one particular case that I just
couldn't sanely fix, and I didn't care enough about it to do something
insane.
So the only _known_ breakage is the "TankItem" profile widget. That's
the bar at the bottom of the profile that shows which cylinder is in
use right now. You'd think that would be trivial to fix up, and yes it
would be - I could just use the regular model of
firstcyl = explicit_first_cylinder(dive, dc)
.. then iterate over the gas change events to see the others ..
but the problem with the "TankItem" widget is that it does its own
model, and it has thrown away the dive and the dive computer
information. It just doesn't even know. It only knows what cylinders
there are, and the plot_info. And it just used to look at the sensor
number in the plot_info, and be done with that. That number no longer
exists.
- I have tested it, and I think the code is better, but hey, it's a
fairly large patch to some of the more complex code in our code base.
That "interpolate missing pressure fields" code really isn't pretty. It
may be prettier, but..
Anyway, without further ado, here's the patch. No sign-off yet, because I
do think people should look and comment. But I think the patch is fine,
and I'll fix anythign that anybody can find, *except* for that TankItem
thing that I will refuse to touch. That class is ugly. It needs to have
access to the actual dive.
Note how it actually does remove more lines than it adds, and that's
despite added comments etc. The code really is simpler, but there may be
cases in there that need more work.
Known missing pieces that don't currently take advantage of concurrent
cylinder pressure data:
- the momentary SAC rate coloring for dives will need more work
- dive merging (but we expect to generally normally not merge dive
computers, which is the main source of sensor data)
- actually taking advantage of different sensor data from different
dive computers
But most of all: Testing. Lots and lots of testing to find all the
corner cases.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-27 17:17:05 +00:00
|
|
|
|
2014-01-17 17:34:15 +00:00
|
|
|
setPolygon(boundingPoly);
|
2014-01-23 17:37:50 +00:00
|
|
|
qDeleteAll(texts);
|
|
|
|
texts.clear();
|
Profile support for multiple concurrent pressure sensors
This finally handles multiple cylinder pressures, both overlapping and
consecutive, and it seems to work on the nasty cases I've thrown at it.
Want to just track five different cylinders all at once, without any
pesky gas switch events? Sure, you can do that. It will show five
different gas pressures for your five cylinders, and they will go down
as you breathe down the cylinders.
I obviously don't have any real data for that case, but I do have a test
file with five actual cylinders that all have samples over the whole
course of the dive. The end result looks messy as hell, but what did
you expect?
HOWEVER.
The only way to do this sanely was
- actually make the "struct plot_info" have all the cylinder pressures
(so no "sensor index and pressure" - every cylinder has a pressure for
every plot info entry)
This obviously makes the plot_info much bigger. We used to have
MAX_CYLINDERS be a fairly generous 8, which seems sane. The planning
code made that 8 be 20. That seems questionable. But whatever.
The good news is that the plot-info should hopefully get freed, and
only be allocated one dive at a time, so the fact that it is big and
nasty shouldn't be a scaling issue, though.
- the "populate_pressure_information()" function had to be rewritten
quite a bit. The good news is that it's actually simpler now, although
I would not go so far as to really call it simple. It's still
complicated and suble, but now it explicitly just does one cylinder at
a time.
It *used* to have this insanely complicated "keep track of the pressure
ranges for every cylinder at once". I just couldn't stand that model
and keep my sanity, so it now just tracks one cylinder at a time, and
doesn't have an array of live data, instead the caller will just call
it for each cylinder.
- get rid of some of our hackier stuff, like the code that populates the
plot_info data code with the currently selected cylinder number, and
clears out any other pressures. That obviously does *not* work when you
may not have a single primary cylinder any more.
Now, the above sounds like all good things. Yeah, it mostly is.
BUT.
There's a few big downsides from the above:
- there's no sane way to do this as a series of small changes.
The change to make the plot_info take an array of cylinder pressures
rather than the sensor+pressure model really isn't amenable to "fix up
one use at a time". When you switch over to the new data structure
model, you have to switch over to the new way of populating the
pressure ranges. The two just go hand in hand.
- Some of our code *depended* on the "sensor+pressure" model. I fixed all
the ones I could sanely fix. There was one particular case that I just
couldn't sanely fix, and I didn't care enough about it to do something
insane.
So the only _known_ breakage is the "TankItem" profile widget. That's
the bar at the bottom of the profile that shows which cylinder is in
use right now. You'd think that would be trivial to fix up, and yes it
would be - I could just use the regular model of
firstcyl = explicit_first_cylinder(dive, dc)
.. then iterate over the gas change events to see the others ..
but the problem with the "TankItem" widget is that it does its own
model, and it has thrown away the dive and the dive computer
information. It just doesn't even know. It only knows what cylinders
there are, and the plot_info. And it just used to look at the sensor
number in the plot_info, and be done with that. That number no longer
exists.
- I have tested it, and I think the code is better, but hey, it's a
fairly large patch to some of the more complex code in our code base.
That "interpolate missing pressure fields" code really isn't pretty. It
may be prettier, but..
Anyway, without further ado, here's the patch. No sign-off yet, because I
do think people should look and comment. But I think the patch is fine,
and I'll fix anythign that anybody can find, *except* for that TankItem
thing that I will refuse to touch. That class is ugly. It needs to have
access to the actual dive.
Note how it actually does remove more lines than it adds, and that's
despite added comments etc. The code really is simpler, but there may be
cases in there that need more work.
Known missing pieces that don't currently take advantage of concurrent
cylinder pressure data:
- the momentary SAC rate coloring for dives will need more work
- dive merging (but we expect to generally normally not merge dive
computers, which is the main source of sensor data)
- actually taking advantage of different sensor data from different
dive computers
But most of all: Testing. Lots and lots of testing to find all the
corner cases.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-27 17:17:05 +00:00
|
|
|
|
2021-10-09 12:56:12 +00:00
|
|
|
// These are offset values used to print the gas labels and pressures on a
|
2017-08-02 15:53:10 +00:00
|
|
|
// dive profile at appropriate Y-coordinates. We alternate aligning the
|
|
|
|
// label and the gas pressure above and under the pressure line.
|
|
|
|
// The values are historical, and we could try to pick the over/under
|
|
|
|
// depending on whether this pressure is higher or lower than the average.
|
|
|
|
// Right now it's just strictly alternating when you have multiple gas
|
|
|
|
// pressures.
|
2015-01-23 18:54:12 +00:00
|
|
|
|
Profile support for multiple concurrent pressure sensors
This finally handles multiple cylinder pressures, both overlapping and
consecutive, and it seems to work on the nasty cases I've thrown at it.
Want to just track five different cylinders all at once, without any
pesky gas switch events? Sure, you can do that. It will show five
different gas pressures for your five cylinders, and they will go down
as you breathe down the cylinders.
I obviously don't have any real data for that case, but I do have a test
file with five actual cylinders that all have samples over the whole
course of the dive. The end result looks messy as hell, but what did
you expect?
HOWEVER.
The only way to do this sanely was
- actually make the "struct plot_info" have all the cylinder pressures
(so no "sensor index and pressure" - every cylinder has a pressure for
every plot info entry)
This obviously makes the plot_info much bigger. We used to have
MAX_CYLINDERS be a fairly generous 8, which seems sane. The planning
code made that 8 be 20. That seems questionable. But whatever.
The good news is that the plot-info should hopefully get freed, and
only be allocated one dive at a time, so the fact that it is big and
nasty shouldn't be a scaling issue, though.
- the "populate_pressure_information()" function had to be rewritten
quite a bit. The good news is that it's actually simpler now, although
I would not go so far as to really call it simple. It's still
complicated and suble, but now it explicitly just does one cylinder at
a time.
It *used* to have this insanely complicated "keep track of the pressure
ranges for every cylinder at once". I just couldn't stand that model
and keep my sanity, so it now just tracks one cylinder at a time, and
doesn't have an array of live data, instead the caller will just call
it for each cylinder.
- get rid of some of our hackier stuff, like the code that populates the
plot_info data code with the currently selected cylinder number, and
clears out any other pressures. That obviously does *not* work when you
may not have a single primary cylinder any more.
Now, the above sounds like all good things. Yeah, it mostly is.
BUT.
There's a few big downsides from the above:
- there's no sane way to do this as a series of small changes.
The change to make the plot_info take an array of cylinder pressures
rather than the sensor+pressure model really isn't amenable to "fix up
one use at a time". When you switch over to the new data structure
model, you have to switch over to the new way of populating the
pressure ranges. The two just go hand in hand.
- Some of our code *depended* on the "sensor+pressure" model. I fixed all
the ones I could sanely fix. There was one particular case that I just
couldn't sanely fix, and I didn't care enough about it to do something
insane.
So the only _known_ breakage is the "TankItem" profile widget. That's
the bar at the bottom of the profile that shows which cylinder is in
use right now. You'd think that would be trivial to fix up, and yes it
would be - I could just use the regular model of
firstcyl = explicit_first_cylinder(dive, dc)
.. then iterate over the gas change events to see the others ..
but the problem with the "TankItem" widget is that it does its own
model, and it has thrown away the dive and the dive computer
information. It just doesn't even know. It only knows what cylinders
there are, and the plot_info. And it just used to look at the sensor
number in the plot_info, and be done with that. That number no longer
exists.
- I have tested it, and I think the code is better, but hey, it's a
fairly large patch to some of the more complex code in our code base.
That "interpolate missing pressure fields" code really isn't pretty. It
may be prettier, but..
Anyway, without further ado, here's the patch. No sign-off yet, because I
do think people should look and comment. But I think the patch is fine,
and I'll fix anythign that anybody can find, *except* for that TankItem
thing that I will refuse to touch. That class is ugly. It needs to have
access to the actual dive.
Note how it actually does remove more lines than it adds, and that's
despite added comments etc. The code really is simpler, but there may be
cases in there that need more work.
Known missing pieces that don't currently take advantage of concurrent
cylinder pressure data:
- the momentary SAC rate coloring for dives will need more work
- dive merging (but we expect to generally normally not merge dive
computers, which is the main source of sensor data)
- actually taking advantage of different sensor data from different
dive computers
But most of all: Testing. Lots and lots of testing to find all the
corner cases.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-27 17:17:05 +00:00
|
|
|
QFlags<Qt::AlignmentFlag> alignVar = Qt::AlignTop;
|
2019-08-04 16:44:57 +00:00
|
|
|
std::vector<QFlags<Qt::AlignmentFlag>> align(pInfo->nr_cylinders);
|
Profile support for multiple concurrent pressure sensors
This finally handles multiple cylinder pressures, both overlapping and
consecutive, and it seems to work on the nasty cases I've thrown at it.
Want to just track five different cylinders all at once, without any
pesky gas switch events? Sure, you can do that. It will show five
different gas pressures for your five cylinders, and they will go down
as you breathe down the cylinders.
I obviously don't have any real data for that case, but I do have a test
file with five actual cylinders that all have samples over the whole
course of the dive. The end result looks messy as hell, but what did
you expect?
HOWEVER.
The only way to do this sanely was
- actually make the "struct plot_info" have all the cylinder pressures
(so no "sensor index and pressure" - every cylinder has a pressure for
every plot info entry)
This obviously makes the plot_info much bigger. We used to have
MAX_CYLINDERS be a fairly generous 8, which seems sane. The planning
code made that 8 be 20. That seems questionable. But whatever.
The good news is that the plot-info should hopefully get freed, and
only be allocated one dive at a time, so the fact that it is big and
nasty shouldn't be a scaling issue, though.
- the "populate_pressure_information()" function had to be rewritten
quite a bit. The good news is that it's actually simpler now, although
I would not go so far as to really call it simple. It's still
complicated and suble, but now it explicitly just does one cylinder at
a time.
It *used* to have this insanely complicated "keep track of the pressure
ranges for every cylinder at once". I just couldn't stand that model
and keep my sanity, so it now just tracks one cylinder at a time, and
doesn't have an array of live data, instead the caller will just call
it for each cylinder.
- get rid of some of our hackier stuff, like the code that populates the
plot_info data code with the currently selected cylinder number, and
clears out any other pressures. That obviously does *not* work when you
may not have a single primary cylinder any more.
Now, the above sounds like all good things. Yeah, it mostly is.
BUT.
There's a few big downsides from the above:
- there's no sane way to do this as a series of small changes.
The change to make the plot_info take an array of cylinder pressures
rather than the sensor+pressure model really isn't amenable to "fix up
one use at a time". When you switch over to the new data structure
model, you have to switch over to the new way of populating the
pressure ranges. The two just go hand in hand.
- Some of our code *depended* on the "sensor+pressure" model. I fixed all
the ones I could sanely fix. There was one particular case that I just
couldn't sanely fix, and I didn't care enough about it to do something
insane.
So the only _known_ breakage is the "TankItem" profile widget. That's
the bar at the bottom of the profile that shows which cylinder is in
use right now. You'd think that would be trivial to fix up, and yes it
would be - I could just use the regular model of
firstcyl = explicit_first_cylinder(dive, dc)
.. then iterate over the gas change events to see the others ..
but the problem with the "TankItem" widget is that it does its own
model, and it has thrown away the dive and the dive computer
information. It just doesn't even know. It only knows what cylinders
there are, and the plot_info. And it just used to look at the sensor
number in the plot_info, and be done with that. That number no longer
exists.
- I have tested it, and I think the code is better, but hey, it's a
fairly large patch to some of the more complex code in our code base.
That "interpolate missing pressure fields" code really isn't pretty. It
may be prettier, but..
Anyway, without further ado, here's the patch. No sign-off yet, because I
do think people should look and comment. But I think the patch is fine,
and I'll fix anythign that anybody can find, *except* for that TankItem
thing that I will refuse to touch. That class is ugly. It needs to have
access to the actual dive.
Note how it actually does remove more lines than it adds, and that's
despite added comments etc. The code really is simpler, but there may be
cases in there that need more work.
Known missing pieces that don't currently take advantage of concurrent
cylinder pressure data:
- the momentary SAC rate coloring for dives will need more work
- dive merging (but we expect to generally normally not merge dive
computers, which is the main source of sensor data)
- actually taking advantage of different sensor data from different
dive computers
But most of all: Testing. Lots and lots of testing to find all the
corner cases.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-27 17:17:05 +00:00
|
|
|
|
2021-10-09 08:33:22 +00:00
|
|
|
double labelHeight = DiveTextItem::fontHeight(dpr, 1.0);
|
Profile support for multiple concurrent pressure sensors
This finally handles multiple cylinder pressures, both overlapping and
consecutive, and it seems to work on the nasty cases I've thrown at it.
Want to just track five different cylinders all at once, without any
pesky gas switch events? Sure, you can do that. It will show five
different gas pressures for your five cylinders, and they will go down
as you breathe down the cylinders.
I obviously don't have any real data for that case, but I do have a test
file with five actual cylinders that all have samples over the whole
course of the dive. The end result looks messy as hell, but what did
you expect?
HOWEVER.
The only way to do this sanely was
- actually make the "struct plot_info" have all the cylinder pressures
(so no "sensor index and pressure" - every cylinder has a pressure for
every plot info entry)
This obviously makes the plot_info much bigger. We used to have
MAX_CYLINDERS be a fairly generous 8, which seems sane. The planning
code made that 8 be 20. That seems questionable. But whatever.
The good news is that the plot-info should hopefully get freed, and
only be allocated one dive at a time, so the fact that it is big and
nasty shouldn't be a scaling issue, though.
- the "populate_pressure_information()" function had to be rewritten
quite a bit. The good news is that it's actually simpler now, although
I would not go so far as to really call it simple. It's still
complicated and suble, but now it explicitly just does one cylinder at
a time.
It *used* to have this insanely complicated "keep track of the pressure
ranges for every cylinder at once". I just couldn't stand that model
and keep my sanity, so it now just tracks one cylinder at a time, and
doesn't have an array of live data, instead the caller will just call
it for each cylinder.
- get rid of some of our hackier stuff, like the code that populates the
plot_info data code with the currently selected cylinder number, and
clears out any other pressures. That obviously does *not* work when you
may not have a single primary cylinder any more.
Now, the above sounds like all good things. Yeah, it mostly is.
BUT.
There's a few big downsides from the above:
- there's no sane way to do this as a series of small changes.
The change to make the plot_info take an array of cylinder pressures
rather than the sensor+pressure model really isn't amenable to "fix up
one use at a time". When you switch over to the new data structure
model, you have to switch over to the new way of populating the
pressure ranges. The two just go hand in hand.
- Some of our code *depended* on the "sensor+pressure" model. I fixed all
the ones I could sanely fix. There was one particular case that I just
couldn't sanely fix, and I didn't care enough about it to do something
insane.
So the only _known_ breakage is the "TankItem" profile widget. That's
the bar at the bottom of the profile that shows which cylinder is in
use right now. You'd think that would be trivial to fix up, and yes it
would be - I could just use the regular model of
firstcyl = explicit_first_cylinder(dive, dc)
.. then iterate over the gas change events to see the others ..
but the problem with the "TankItem" widget is that it does its own
model, and it has thrown away the dive and the dive computer
information. It just doesn't even know. It only knows what cylinders
there are, and the plot_info. And it just used to look at the sensor
number in the plot_info, and be done with that. That number no longer
exists.
- I have tested it, and I think the code is better, but hey, it's a
fairly large patch to some of the more complex code in our code base.
That "interpolate missing pressure fields" code really isn't pretty. It
may be prettier, but..
Anyway, without further ado, here's the patch. No sign-off yet, because I
do think people should look and comment. But I think the patch is fine,
and I'll fix anythign that anybody can find, *except* for that TankItem
thing that I will refuse to touch. That class is ugly. It needs to have
access to the actual dive.
Note how it actually does remove more lines than it adds, and that's
despite added comments etc. The code really is simpler, but there may be
cases in there that need more work.
Known missing pieces that don't currently take advantage of concurrent
cylinder pressure data:
- the momentary SAC rate coloring for dives will need more work
- dive merging (but we expect to generally normally not merge dive
computers, which is the main source of sensor data)
- actually taking advantage of different sensor data from different
dive computers
But most of all: Testing. Lots and lots of testing to find all the
corner cases.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-27 17:17:05 +00:00
|
|
|
|
2021-10-09 12:56:12 +00:00
|
|
|
for (const Segment &segment: segments) {
|
|
|
|
// Magic Y offset depending on whether we're aliging
|
|
|
|
// the top of the text or the bottom of the text to
|
|
|
|
// the pressure line.
|
|
|
|
double value_y_offset = -0.5 * dpr;
|
|
|
|
double label_y_offset = alignVar & Qt::AlignTop ? labelHeight : -labelHeight;
|
|
|
|
gasmix gas = get_cylinder(d, segment.cyl)->gasmix;
|
|
|
|
plotPressureValue(segment.first.pressure, segment.first.time, alignVar, value_y_offset);
|
|
|
|
plotGasValue(segment.first.pressure, segment.first.time, gas, alignVar, label_y_offset);
|
|
|
|
|
|
|
|
// For each cylinder, on right hand side of the curve, write cylinder pressure
|
|
|
|
plotPressureValue(segment.last.pressure, segment.last.time, alignVar | Qt::AlignLeft, value_y_offset);
|
|
|
|
|
|
|
|
/* Alternate alignment as we see cylinder use.. */
|
|
|
|
alignVar ^= Qt::AlignTop | Qt::AlignBottom;
|
2014-01-21 16:05:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-09 12:56:12 +00:00
|
|
|
void DiveGasPressureItem::plotPressureValue(double mbar, double sec, QFlags<Qt::AlignmentFlag> align, double pressure_offset)
|
2014-01-21 16:05:29 +00:00
|
|
|
{
|
|
|
|
const char *unit;
|
2021-10-09 12:56:12 +00:00
|
|
|
int pressure = get_pressure_units(lrint(mbar), &unit);
|
2021-08-12 20:57:57 +00:00
|
|
|
DiveTextItem *text = new DiveTextItem(dpr, 1.0, align, this);
|
2021-08-13 07:56:46 +00:00
|
|
|
text->set(QString("%1%2").arg(pressure).arg(unit), getColor(PRESSURE_TEXT));
|
2021-10-09 08:33:22 +00:00
|
|
|
text->setPos(hAxis.posAtValue(sec), vAxis.posAtValue(mbar) + pressure_offset);
|
2014-01-21 16:05:29 +00:00
|
|
|
texts.push_back(text);
|
2014-01-17 17:34:15 +00:00
|
|
|
}
|
|
|
|
|
2021-10-09 12:56:12 +00:00
|
|
|
void DiveGasPressureItem::plotGasValue(double mbar, double sec, struct gasmix gasmix, QFlags<Qt::AlignmentFlag> align, double gasname_offset)
|
2014-01-21 16:14:52 +00:00
|
|
|
{
|
2015-05-28 19:23:49 +00:00
|
|
|
QString gas = get_gas_string(gasmix);
|
2021-08-12 20:57:57 +00:00
|
|
|
DiveTextItem *text = new DiveTextItem(dpr, 1.0, align, this);
|
2021-08-13 07:56:46 +00:00
|
|
|
text->set(gas, getColor(PRESSURE_TEXT));
|
2021-10-09 08:33:22 +00:00
|
|
|
text->setPos(hAxis.posAtValue(sec), vAxis.posAtValue(mbar) + gasname_offset);
|
2014-01-21 16:14:52 +00:00
|
|
|
texts.push_back(text);
|
|
|
|
}
|
|
|
|
|
2018-05-21 16:20:29 +00:00
|
|
|
void DiveGasPressureItem::paint(QPainter *painter, const QStyleOptionGraphicsItem*, QWidget*)
|
2014-01-17 17:34:15 +00:00
|
|
|
{
|
2014-02-28 04:09:57 +00:00
|
|
|
if (polygon().isEmpty())
|
2014-02-10 16:41:59 +00:00
|
|
|
return;
|
2014-01-17 17:34:15 +00:00
|
|
|
QPen pen;
|
|
|
|
pen.setCosmetic(true);
|
|
|
|
pen.setWidth(2);
|
2014-07-17 23:18:14 +00:00
|
|
|
painter->save();
|
2021-10-09 12:56:12 +00:00
|
|
|
for (const Segment &segment: segments) {
|
|
|
|
for (size_t i = 1; i < segment.polygon.size(); i++) {
|
|
|
|
pen.setBrush(segment.polygon[i].col);
|
2014-01-17 17:34:15 +00:00
|
|
|
painter->setPen(pen);
|
2021-10-09 12:56:12 +00:00
|
|
|
painter->drawLine(segment.polygon[i - 1].pos, segment.polygon[i].pos);
|
2014-01-17 17:34:15 +00:00
|
|
|
}
|
|
|
|
}
|
2014-07-17 23:18:14 +00:00
|
|
|
painter->restore();
|
2014-01-17 19:54:47 +00:00
|
|
|
}
|
2014-01-21 16:59:19 +00:00
|
|
|
|
2021-06-03 15:19:38 +00:00
|
|
|
DiveCalculatedCeiling::DiveCalculatedCeiling(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, int hColumn,
|
2021-08-09 14:48:08 +00:00
|
|
|
const DiveCartesianAxis &vAxis, int vColumn, double dpr) :
|
|
|
|
AbstractProfilePolygonItem(model, hAxis, hColumn, vAxis, vColumn, dpr)
|
2014-01-29 12:53:40 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-10-08 19:00:37 +00:00
|
|
|
void DiveCalculatedCeiling::replot(const dive *d, int from, int to, bool in_planner)
|
2014-01-21 16:59:19 +00:00
|
|
|
{
|
2021-10-08 19:00:37 +00:00
|
|
|
makePolygon(from, to);
|
2014-01-21 16:59:19 +00:00
|
|
|
|
|
|
|
QLinearGradient pat(0, polygon().boundingRect().top(), 0, polygon().boundingRect().bottom());
|
|
|
|
pat.setColorAt(0, getColor(CALC_CEILING_SHALLOW));
|
|
|
|
pat.setColorAt(1, getColor(CALC_CEILING_DEEP));
|
2014-02-28 04:09:57 +00:00
|
|
|
setPen(QPen(QBrush(Qt::NoBrush), 0));
|
2014-01-21 16:59:19 +00:00
|
|
|
setBrush(pat);
|
|
|
|
}
|
|
|
|
|
2014-02-28 04:09:57 +00:00
|
|
|
void DiveCalculatedCeiling::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
2014-01-21 16:59:19 +00:00
|
|
|
{
|
2014-02-28 04:09:57 +00:00
|
|
|
if (polygon().isEmpty())
|
2014-02-10 16:41:59 +00:00
|
|
|
return;
|
2014-01-21 16:59:19 +00:00
|
|
|
QGraphicsPolygonItem::paint(painter, option, widget);
|
|
|
|
}
|
2014-01-21 19:34:36 +00:00
|
|
|
|
2021-06-03 15:19:38 +00:00
|
|
|
DiveCalculatedTissue::DiveCalculatedTissue(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, int hColumn,
|
2021-08-09 14:48:08 +00:00
|
|
|
const DiveCartesianAxis &vAxis, int vColumn, double dpr) :
|
|
|
|
DiveCalculatedCeiling(model, hAxis, hColumn, vAxis, vColumn, dpr)
|
2014-01-22 21:22:07 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-06-03 15:19:38 +00:00
|
|
|
DiveReportedCeiling::DiveReportedCeiling(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, int hColumn,
|
2021-08-09 14:48:08 +00:00
|
|
|
const DiveCartesianAxis &vAxis, int vColumn, double dpr) :
|
|
|
|
AbstractProfilePolygonItem(model, hAxis, hColumn, vAxis, vColumn, dpr)
|
2016-09-24 19:38:24 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-10-09 12:56:12 +00:00
|
|
|
std::pair<double,double> DiveReportedCeiling::getTimeValue(int i) const
|
|
|
|
{
|
|
|
|
const plot_data &entry = dataModel.data().entry[i];
|
|
|
|
int value = entry.in_deco && entry.stopdepth ? std::min(entry.stopdepth, entry.depth) : 0;
|
|
|
|
return { static_cast<double>(entry.sec), static_cast<double>(value) };
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<double, double> DiveReportedCeiling::getPoint(int i) const
|
|
|
|
{
|
|
|
|
auto [x,y] = getTimeValue(i);
|
|
|
|
if (i == from && i < to) {
|
|
|
|
auto [next_x, next_y] = getTimeValue(i + 1);
|
|
|
|
clipStart(x, y, next_x, next_y);
|
|
|
|
}
|
|
|
|
if (i == to - 1 && i > 0) {
|
|
|
|
auto [prev_x, prev_y] = getTimeValue(i - 1);
|
|
|
|
clipStop(x, y, prev_x, prev_y);
|
|
|
|
}
|
|
|
|
return { x, y };
|
|
|
|
}
|
|
|
|
|
2021-10-08 19:00:37 +00:00
|
|
|
void DiveReportedCeiling::replot(const dive *, int fromIn, int toIn, bool)
|
2014-01-21 19:34:36 +00:00
|
|
|
{
|
2021-10-08 19:00:37 +00:00
|
|
|
from = fromIn;
|
|
|
|
to = toIn;
|
|
|
|
|
2014-01-21 19:34:36 +00:00
|
|
|
QPolygonF p;
|
2021-10-08 19:00:37 +00:00
|
|
|
for (int i = from; i < to; i++) {
|
2021-10-09 12:56:12 +00:00
|
|
|
auto [sec, value] = getPoint(i);
|
2021-10-08 19:00:37 +00:00
|
|
|
if (i == from)
|
2021-10-09 12:56:12 +00:00
|
|
|
p.append(QPointF(hAxis.posAtValue(sec), vAxis.posAtValue(0.0)));
|
|
|
|
p.append(QPointF(hAxis.posAtValue(sec), vAxis.posAtValue(value)));
|
|
|
|
if (i == to - 1)
|
|
|
|
p.append(QPointF(hAxis.posAtValue(sec), vAxis.posAtValue(0)));
|
2014-01-21 19:34:36 +00:00
|
|
|
}
|
|
|
|
setPolygon(p);
|
|
|
|
QLinearGradient pat(0, p.boundingRect().top(), 0, p.boundingRect().bottom());
|
2014-04-15 05:52:22 +00:00
|
|
|
// does the user want the ceiling in "surface color" or in red?
|
2014-04-16 20:03:44 +00:00
|
|
|
if (prefs.redceiling) {
|
2014-04-15 05:52:22 +00:00
|
|
|
pat.setColorAt(0, getColor(CEILING_SHALLOW));
|
|
|
|
pat.setColorAt(1, getColor(CEILING_DEEP));
|
|
|
|
} else {
|
|
|
|
pat.setColorAt(0, getColor(BACKGROUND_TRANS));
|
|
|
|
pat.setColorAt(1, getColor(BACKGROUND_TRANS));
|
|
|
|
}
|
2014-02-28 04:09:57 +00:00
|
|
|
setPen(QPen(QBrush(Qt::NoBrush), 0));
|
2014-01-21 19:34:36 +00:00
|
|
|
setBrush(pat);
|
|
|
|
}
|
|
|
|
|
2014-02-28 04:09:57 +00:00
|
|
|
void DiveReportedCeiling::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
2014-01-21 19:34:36 +00:00
|
|
|
{
|
2014-02-28 04:09:57 +00:00
|
|
|
if (polygon().isEmpty())
|
2014-02-10 16:41:59 +00:00
|
|
|
return;
|
2014-01-21 19:34:36 +00:00
|
|
|
QGraphicsPolygonItem::paint(painter, option, widget);
|
2014-01-22 17:08:19 +00:00
|
|
|
}
|
2014-01-23 19:54:34 +00:00
|
|
|
|
2021-10-08 19:00:37 +00:00
|
|
|
void PartialPressureGasItem::replot(const dive *, int fromIn, int toIn, bool)
|
2014-01-23 19:54:34 +00:00
|
|
|
{
|
2021-10-08 19:00:37 +00:00
|
|
|
from = fromIn;
|
|
|
|
to = toIn;
|
|
|
|
|
2014-01-23 19:54:34 +00:00
|
|
|
QPolygonF poly;
|
2014-03-15 18:00:58 +00:00
|
|
|
QPolygonF alertpoly;
|
|
|
|
alertPolygons.clear();
|
2017-03-25 10:15:13 +00:00
|
|
|
double threshold_min = 100.0; // yes, a ridiculous high partial pressure
|
|
|
|
double threshold_max = 0.0;
|
|
|
|
if (thresholdPtrMax)
|
|
|
|
threshold_max = *thresholdPtrMax;
|
|
|
|
if (thresholdPtrMin)
|
|
|
|
threshold_min = *thresholdPtrMin;
|
2014-03-15 18:00:58 +00:00
|
|
|
bool inAlertFragment = false;
|
2021-10-09 12:56:12 +00:00
|
|
|
for (int i = from; i < to; i++) {
|
|
|
|
auto [time, value] = getPoint(i);
|
2020-12-20 17:12:55 +00:00
|
|
|
QPointF point(hAxis.posAtValue(time), vAxis.posAtValue(value));
|
2014-02-28 04:09:57 +00:00
|
|
|
poly.push_back(point);
|
2017-03-25 10:15:13 +00:00
|
|
|
if (thresholdPtrMax && value >= threshold_max) {
|
|
|
|
if (inAlertFragment) {
|
|
|
|
alertPolygons.back().push_back(point);
|
|
|
|
} else {
|
|
|
|
alertpoly.clear();
|
|
|
|
alertpoly.push_back(point);
|
|
|
|
alertPolygons.append(alertpoly);
|
|
|
|
inAlertFragment = true;
|
|
|
|
}
|
|
|
|
} else if (thresholdPtrMin && value <= threshold_min) {
|
2014-03-15 18:00:58 +00:00
|
|
|
if (inAlertFragment) {
|
|
|
|
alertPolygons.back().push_back(point);
|
|
|
|
} else {
|
|
|
|
alertpoly.clear();
|
|
|
|
alertpoly.push_back(point);
|
|
|
|
alertPolygons.append(alertpoly);
|
|
|
|
inAlertFragment = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
inAlertFragment = false;
|
|
|
|
}
|
2014-01-23 19:54:34 +00:00
|
|
|
}
|
|
|
|
setPolygon(poly);
|
|
|
|
}
|
2014-01-27 17:14:42 +00:00
|
|
|
|
2018-05-21 16:20:29 +00:00
|
|
|
void PartialPressureGasItem::paint(QPainter *painter, const QStyleOptionGraphicsItem*, QWidget*)
|
2014-01-27 17:14:42 +00:00
|
|
|
{
|
2014-03-19 16:24:42 +00:00
|
|
|
const qreal pWidth = 0.0;
|
2014-07-17 23:18:14 +00:00
|
|
|
painter->save();
|
2014-03-19 16:24:42 +00:00
|
|
|
painter->setPen(QPen(normalColor, pWidth));
|
2014-01-23 19:54:34 +00:00
|
|
|
painter->drawPolyline(polygon());
|
2014-03-15 18:00:58 +00:00
|
|
|
|
|
|
|
QPolygonF poly;
|
2014-03-19 16:24:42 +00:00
|
|
|
painter->setPen(QPen(alertColor, pWidth));
|
2014-05-22 18:40:22 +00:00
|
|
|
Q_FOREACH (const QPolygonF &poly, alertPolygons)
|
2014-03-15 18:00:58 +00:00
|
|
|
painter->drawPolyline(poly);
|
2014-07-17 23:18:14 +00:00
|
|
|
painter->restore();
|
2014-01-23 19:54:34 +00:00
|
|
|
}
|
|
|
|
|
2018-09-02 20:26:19 +00:00
|
|
|
void PartialPressureGasItem::setThresholdSettingsKey(const double *prefPointerMin, const double *prefPointerMax)
|
2014-01-23 19:54:34 +00:00
|
|
|
{
|
2017-03-25 10:15:13 +00:00
|
|
|
thresholdPtrMin = prefPointerMin;
|
|
|
|
thresholdPtrMax = prefPointerMax;
|
2014-01-23 19:54:34 +00:00
|
|
|
}
|
|
|
|
|
2021-06-03 15:19:38 +00:00
|
|
|
PartialPressureGasItem::PartialPressureGasItem(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, int hColumn,
|
2021-08-09 14:48:08 +00:00
|
|
|
const DiveCartesianAxis &vAxis, int vColumn, double dpr) :
|
|
|
|
AbstractProfilePolygonItem(model, hAxis, hColumn, vAxis, vColumn, dpr),
|
2017-03-25 10:15:13 +00:00
|
|
|
thresholdPtrMin(NULL),
|
|
|
|
thresholdPtrMax(NULL)
|
2014-01-23 19:54:34 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-02-28 04:09:57 +00:00
|
|
|
void PartialPressureGasItem::setColors(const QColor &normal, const QColor &alert)
|
2014-01-27 17:14:42 +00:00
|
|
|
{
|
|
|
|
normalColor = normal;
|
|
|
|
alertColor = alert;
|
2014-01-23 19:54:34 +00:00
|
|
|
}
|