Profile: Improve Display of Bailout / Loop Events.

For dives in CCR mode, show 'bailout' and 'on loop' events whenever a
gas switch from a diluent gas to a bailout gas and vice versa happens.

Signed-off-by: Michael Keller <github@ike.ch>
This commit is contained in:
Michael Keller 2024-09-22 23:24:25 +12:00
parent de12d3a6ea
commit 07898f277c
16 changed files with 183 additions and 121 deletions

View file

@ -16,7 +16,7 @@
static int depthAtTime(const plot_info &pi, duration_t time);
DiveEventItem::DiveEventItem(const struct dive *d, int idx, const struct event &ev, struct gasmix lastgasmix,
DiveEventItem::DiveEventItem(const struct dive *d, const struct divecomputer *dc, int idx, const struct event &ev, const struct gasmix lastgasmix, divemode_t lastdivemode,
const plot_info &pi, DiveCartesianAxis *hAxis, DiveCartesianAxis *vAxis,
int speed, const DivePixmaps &pixmaps, QGraphicsItem *parent) : DivePixmapItem(parent),
vAxis(vAxis),
@ -28,8 +28,8 @@ DiveEventItem::DiveEventItem(const struct dive *d, int idx, const struct event &
{
setFlag(ItemIgnoresTransformations);
setupPixmap(lastgasmix, pixmaps);
setupToolTipString(lastgasmix);
setupPixmap(lastgasmix, lastdivemode, *dc, pixmaps);
setupToolTipString(lastgasmix, lastdivemode, *dc);
recalculatePos();
}
@ -37,7 +37,7 @@ DiveEventItem::~DiveEventItem()
{
}
void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pixmaps)
void DiveEventItem::setupPixmap(const struct gasmix lastgasmix, divemode_t lastdivemode, const struct divecomputer &dc, const DivePixmaps &pixmaps)
{
event_severity severity = ev.get_severity();
if (ev.name.empty()) {
@ -51,10 +51,15 @@ void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pix
setPixmap(pixmaps.bookmark);
setOffset(QPointF(0.0, -pixmap().height()));
} else if (ev.is_gaschange()) {
struct gasmix mix = dive->get_gasmix_from_event(ev);
auto [mix, divemode] = dive->get_gasmix_from_event(ev, dc);
struct icd_data icd_data;
bool icd = isobaric_counterdiffusion(lastgasmix, mix, &icd_data);
if (mix.he.permille) {
if (divemode != lastdivemode) {
if (divemode == CCR)
setPixmap(pixmaps.onCCRLoop);
else
setPixmap(pixmaps.bailout);
} else if (mix.he.permille) {
if (icd)
setPixmap(pixmaps.gaschangeTrimixICD);
else
@ -111,7 +116,7 @@ void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pix
}
}
void DiveEventItem::setupToolTipString(struct gasmix lastgasmix)
void DiveEventItem::setupToolTipString(const struct gasmix lastgasmix, divemode_t lastdivemode, const struct divecomputer &dc)
{
// we display the event on screen - so translate
QString name = gettextFromC::tr(ev.name.c_str());
@ -120,7 +125,7 @@ void DiveEventItem::setupToolTipString(struct gasmix lastgasmix)
if (ev.is_gaschange()) {
struct icd_data icd_data;
struct gasmix mix = dive->get_gasmix_from_event(ev);
auto [mix, divemode] = dive->get_gasmix_from_event(ev, dc);
name += ": ";
name += QString::fromStdString(mix.name());
@ -135,6 +140,9 @@ void DiveEventItem::setupToolTipString(struct gasmix lastgasmix)
qPrintable(tr("ΔN₂")), icd_data.dN2 / 10.0,
icd ? ">" : "<", lrint(-icd_data.dHe / 5.0) / 10.0);
}
if (divemode != lastdivemode)
name += QString("\nmodechange: %1").arg(gettextFromC::tr(divemode_text_ui[divemode != OC]));
} else if (ev.name == "modechange") {
name += QString(": %1").arg(gettextFromC::tr(divemode_text_ui[ev.value]));
} else if (value) {

View file

@ -13,7 +13,7 @@ struct plot_info;
class DiveEventItem : public DivePixmapItem {
Q_OBJECT
public:
DiveEventItem(const struct dive *d, int idx, const struct event &ev, struct gasmix lastgasmix,
DiveEventItem(const struct dive *d, const struct divecomputer *dc, int idx, const struct event &ev, const struct gasmix lastgasmix, divemode_t lastdivemode,
const struct plot_info &pi, DiveCartesianAxis *hAxis, DiveCartesianAxis *vAxis,
int speed, const DivePixmaps &pixmaps, QGraphicsItem *parent = nullptr);
~DiveEventItem();
@ -25,8 +25,8 @@ public:
int firstSecond, int lastSecond);
private:
void setupToolTipString(struct gasmix lastgasmix);
void setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pixmaps);
void setupToolTipString(struct gasmix lastgasmix, divemode_t lastdivemode, const struct divecomputer &dc);
void setupPixmap(struct gasmix lastgasmix, divemode_t lastdivemode, const struct divecomputer &dc, const DivePixmaps &pixmaps);
void recalculatePos();
DiveCartesianAxis *vAxis;
DiveCartesianAxis *hAxis;

View file

@ -551,8 +551,17 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM
// while all other items are up there on the constructor.
qDeleteAll(eventItems);
eventItems.clear();
struct gasmix lastgasmix = d->get_gasmix_at_time(*currentdc, 1_sec);
const struct gasmix *lastgasmix;
divemode_t lastdivemode;
int cylinder_index = gasmix_loop(*d, *currentdc).next_cylinder_index().first;
if (cylinder_index == -1) {
lastgasmix = &gasmix_air;
lastdivemode = OC;
} else {
const cylinder_t *cylinder = d->get_cylinder(cylinder_index);
lastgasmix = &cylinder->gasmix;
lastdivemode = get_effective_divemode(*currentdc, *cylinder);
}
for (auto [idx, event]: enumerated_range(currentdc->events)) {
// if print mode is selected only draw headings, SP change, gas events or bookmark event
if (printMode) {
@ -560,18 +569,23 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM
!(event.name == "heading" ||
(event.name == "SP change" && event.time.seconds == 0) ||
event.is_gaschange() ||
event.is_divemodechange() ||
event.type == SAMPLE_EVENT_BOOKMARK))
continue;
}
if (DiveEventItem::isInteresting(d, currentdc, event, plotInfo, firstSecond, lastSecond)) {
auto item = new DiveEventItem(d, idx, event, lastgasmix, plotInfo,
auto item = new DiveEventItem(d, currentdc, idx, event, *lastgasmix, lastdivemode, plotInfo,
timeAxis, profileYAxis, animSpeed, *pixmaps);
item->setZValue(2);
addItem(item);
eventItems.push_back(item);
}
if (event.is_gaschange())
lastgasmix = d->get_gasmix_from_event(event);
if (event.is_gaschange()) {
auto [gasmix, divemode] = d->get_gasmix_from_event(event, *currentdc);
lastgasmix = &gasmix;
lastdivemode = divemode;
} else if (event.is_divemodechange())
lastdivemode = event.value ? CCR : OC;
}
QString dcText = QString::fromStdString(get_dc_nickname(currentdc));

View file

@ -529,10 +529,11 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event)
QMenu m;
if (!d)
return;
const struct divecomputer *currentdc = d->get_dc(dc);
// figure out if we are ontop of the dive computer name in the profile
QGraphicsItem *sceneItem = itemAt(mapFromGlobal(event->globalPos()));
if (isDiveTextItem(sceneItem, profileScene->diveComputerText)) {
const struct divecomputer *currentdc = d->get_dc(dc);
if (!currentdc->deviceid && dc == 0 && d->number_of_computers() == 1)
// nothing to do, can't rename, delete or reorder
return;
@ -561,7 +562,6 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event)
addGasChangeMenu(m, tr("Edit gas change"), *d, dc, item->ev.time.seconds);
} else if (d && d->cylinders.size() > 1) {
// if we have more than one gas, offer to switch to another one
const struct divecomputer *currentdc = d->get_dc(dc);
if (seconds == 0 || (!currentdc->samples.empty() && seconds <= currentdc->samples[0].time.seconds))
addGasChangeMenu(m, tr("Set initial gas"), *d, dc, 0);
else
@ -571,18 +571,21 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event)
m.addAction(tr("Add bookmark"), [this, seconds]() { addBookmark(seconds); });
m.addAction(tr("Split dive into two"), [this, seconds]() { splitDive(seconds); });
divemode_loop loop(*d->get_dc(dc));
divemode_t divemode = loop.at(seconds);
QMenu *changeMode = m.addMenu(tr("Change divemode"));
if (divemode != OC)
changeMode->addAction(gettextFromC::tr(divemode_text_ui[OC]),
[this, seconds](){ addDivemodeSwitch(seconds, OC); });
if (divemode != CCR)
changeMode->addAction(gettextFromC::tr(divemode_text_ui[CCR]),
[this, seconds](){ addDivemodeSwitch(seconds, CCR); });
if (divemode != PSCR)
changeMode->addAction(gettextFromC::tr(divemode_text_ui[PSCR]),
[this, seconds](){ addDivemodeSwitch(seconds, PSCR); });
[[maybe_unused]] auto [divemode, cylinder_index, _gasmix] = get_dive_status_at(*d, *currentdc, seconds);
if (currentdc->divemode == PSCR || (currentdc->divemode == CCR && prefs.allowOcGasAsDiluent && (cylinder_index == -1 || d->get_cylinder(cylinder_index)->cylinder_use == OC_GAS))) {
QMenu *changeMode = m.addMenu(tr("Change divemode"));
if (divemode != OC) {
changeMode->addAction(gettextFromC::tr(divemode_text_ui[OC]),
[this, seconds](){ addDivemodeSwitch(seconds, OC); });
} else {
if (currentdc->divemode == PSCR)
changeMode->addAction(gettextFromC::tr(divemode_text_ui[PSCR]),
[this, seconds](){ addDivemodeSwitch(seconds, PSCR); });
else
changeMode->addAction(gettextFromC::tr(divemode_text_ui[CCR]),
[this, seconds](){ addDivemodeSwitch(seconds, CCR); });
}
}
if (DiveEventItem *item = dynamic_cast<DiveEventItem *>(sceneItem)) {
m.addAction(tr("Remove event"), [this,item] { removeEvent(item); });
@ -639,7 +642,6 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event)
}
m2->addAction(tr("All event types"), this, &ProfileWidget2::unhideEventTypes);
}
const struct divecomputer *currentdc = d->get_dc(dc);
if (currentdc && std::any_of(currentdc->events.begin(), currentdc->events.end(),
[] (auto &ev) { return ev.hidden; }))
m.addAction(tr("Unhide individually hidden events of this dive"), this, &ProfileWidget2::unhideEvents);