mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-19 22:16:15 +00:00
Tissue saturation plot a la Sherwater Pretel
This adds a toolbox icon to turn on a tissue plot inspired by the bar graph of the Sherwater Petrel, It shows the inert gas partial pressures for individual compartments. If they are below the ambient pressure (grey line) they are shown in units of the ambient pressure, if they are above, the excess is shown as a percentage of the allowed overpressure for plain Buehlmann. So it has the same units as a gradient factor. Thus also the a gradient factor line (for the current depth) is shown. The different tissues get different colors, greener for the faster ones and bluer for the slower ones. Positioning and on/off icon action still need some tender loving care. Signed-off-by: Robert C. Helling <helling@atdotde.de> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
parent
7cdf55ffcc
commit
3fc9c1e005
21 changed files with 319 additions and 8 deletions
|
@ -54,6 +54,10 @@ QVariant DivePlotDataModel::data(const QModelIndex &index, int role) const
|
|||
return item.pressures.o2;
|
||||
case HEARTBEAT:
|
||||
return item.heartbeat;
|
||||
case AMBPRESSURE:
|
||||
return AMB_PERCENTAGE;
|
||||
case GFLINE:
|
||||
return item.gfline;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,6 +65,10 @@ QVariant DivePlotDataModel::data(const QModelIndex &index, int role) const
|
|||
return item.ceilings[index.column() - TISSUE_1];
|
||||
}
|
||||
|
||||
if (role == Qt::DisplayRole && index.column() >= PERCENTAGE_1 && index.column() <= PERCENTAGE_16) {
|
||||
return item.percentages[index.column() - PERCENTAGE_1];
|
||||
}
|
||||
|
||||
if (role == Qt::BackgroundRole) {
|
||||
switch (index.column()) {
|
||||
case COLOR:
|
||||
|
@ -117,10 +125,15 @@ QVariant DivePlotDataModel::headerData(int section, Qt::Orientation orientation,
|
|||
return tr("pHe");
|
||||
case PO2:
|
||||
return tr("pO₂");
|
||||
case AMBPRESSURE:
|
||||
return tr("Ambient pressure");
|
||||
}
|
||||
if (role == Qt::DisplayRole && section >= TISSUE_1 && section <= TISSUE_16) {
|
||||
return QString("Ceiling: %1").arg(section - TISSUE_1);
|
||||
}
|
||||
if (role == Qt::DisplayRole && section >= PERCENTAGE_1 && section <= PERCENTAGE_16) {
|
||||
return QString("Tissue: %1").arg(section - PERCENTAGE_1);
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
|
|
|
@ -40,10 +40,28 @@ public:
|
|||
TISSUE_14,
|
||||
TISSUE_15,
|
||||
TISSUE_16,
|
||||
PERCENTAGE_1,
|
||||
PERCENTAGE_2,
|
||||
PERCENTAGE_3,
|
||||
PERCENTAGE_4,
|
||||
PERCENTAGE_5,
|
||||
PERCENTAGE_6,
|
||||
PERCENTAGE_7,
|
||||
PERCENTAGE_8,
|
||||
PERCENTAGE_9,
|
||||
PERCENTAGE_10,
|
||||
PERCENTAGE_11,
|
||||
PERCENTAGE_12,
|
||||
PERCENTAGE_13,
|
||||
PERCENTAGE_14,
|
||||
PERCENTAGE_15,
|
||||
PERCENTAGE_16,
|
||||
PN2,
|
||||
PHE,
|
||||
PO2,
|
||||
HEARTBEAT,
|
||||
AMBPRESSURE,
|
||||
GFLINE,
|
||||
COLUMNS
|
||||
};
|
||||
explicit DivePlotDataModel(QObject *parent = 0);
|
||||
|
|
|
@ -338,6 +338,155 @@ void DiveHeartrateItem::settingsChanged()
|
|||
setVisible(prefs.hrgraph);
|
||||
}
|
||||
|
||||
DivePercentageItem::DivePercentageItem(int i)
|
||||
{
|
||||
QPen pen;
|
||||
QColor color;
|
||||
color.setHsl(100 + 10 * i, 200, 100);
|
||||
pen.setBrush(QBrush(color));
|
||||
pen.setCosmetic(true);
|
||||
pen.setWidth(1);
|
||||
setPen(pen);
|
||||
settingsChanged();
|
||||
}
|
||||
|
||||
void DivePercentageItem::modelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
|
||||
{
|
||||
int last = -300, last_printed_hr = 0, sec = 0;
|
||||
|
||||
// We don't have enougth data to calculate things, quit.
|
||||
if (!shouldCalculateStuff(topLeft, bottomRight))
|
||||
return;
|
||||
|
||||
// Ignore empty values. a heartrate of 0 would be a bad sign.
|
||||
QPolygonF poly;
|
||||
for (int i = 0, modelDataCount = dataModel->rowCount(); i < modelDataCount; i++) {
|
||||
int hr = dataModel->index(i, vDataColumn).data().toInt();
|
||||
if (!hr)
|
||||
continue;
|
||||
sec = dataModel->index(i, hDataColumn).data().toInt();
|
||||
QPointF point(hAxis->posAtValue(sec), vAxis->posAtValue(hr));
|
||||
poly.append(point);
|
||||
}
|
||||
setPolygon(poly);
|
||||
|
||||
if (texts.count())
|
||||
texts.last()->setAlignment(Qt::AlignLeft | Qt::AlignBottom);
|
||||
}
|
||||
|
||||
void DivePercentageItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||
{
|
||||
if (polygon().isEmpty())
|
||||
return;
|
||||
painter->save();
|
||||
painter->setPen(pen());
|
||||
painter->drawPolyline(polygon());
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
void DivePercentageItem::settingsChanged()
|
||||
{
|
||||
setVisible(prefs.percentagegraph);
|
||||
}
|
||||
|
||||
DiveAmbPressureItem::DiveAmbPressureItem()
|
||||
{
|
||||
QPen pen;
|
||||
pen.setBrush(QBrush(getColor(::AMB_PRESSURE_LINE)));
|
||||
pen.setCosmetic(true);
|
||||
pen.setWidth(2);
|
||||
setPen(pen);
|
||||
settingsChanged();
|
||||
}
|
||||
|
||||
void DiveAmbPressureItem::modelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
|
||||
{
|
||||
int last = -300, last_printed_hr = 0, sec = 0;
|
||||
|
||||
// We don't have enougth data to calculate things, quit.
|
||||
if (!shouldCalculateStuff(topLeft, bottomRight))
|
||||
return;
|
||||
|
||||
// Ignore empty values. a heartrate of 0 would be a bad sign.
|
||||
QPolygonF poly;
|
||||
for (int i = 0, modelDataCount = dataModel->rowCount(); i < modelDataCount; i++) {
|
||||
int hr = dataModel->index(i, vDataColumn).data().toInt();
|
||||
if (!hr)
|
||||
continue;
|
||||
sec = dataModel->index(i, hDataColumn).data().toInt();
|
||||
QPointF point(hAxis->posAtValue(sec), vAxis->posAtValue(hr));
|
||||
poly.append(point);
|
||||
}
|
||||
setPolygon(poly);
|
||||
|
||||
if (texts.count())
|
||||
texts.last()->setAlignment(Qt::AlignLeft | Qt::AlignBottom);
|
||||
}
|
||||
|
||||
void DiveAmbPressureItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||
{
|
||||
if (polygon().isEmpty())
|
||||
return;
|
||||
painter->save();
|
||||
painter->setPen(pen());
|
||||
painter->drawPolyline(polygon());
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
void DiveAmbPressureItem::settingsChanged()
|
||||
{
|
||||
setVisible(prefs.percentagegraph);
|
||||
}
|
||||
|
||||
DiveGFLineItem::DiveGFLineItem()
|
||||
{
|
||||
QPen pen;
|
||||
pen.setBrush(QBrush(getColor(::GF_LINE)));
|
||||
pen.setCosmetic(true);
|
||||
pen.setWidth(2);
|
||||
setPen(pen);
|
||||
settingsChanged();
|
||||
}
|
||||
|
||||
void DiveGFLineItem::modelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
|
||||
{
|
||||
int last = -300, last_printed_hr = 0, sec = 0;
|
||||
|
||||
// We don't have enougth data to calculate things, quit.
|
||||
if (!shouldCalculateStuff(topLeft, bottomRight))
|
||||
return;
|
||||
|
||||
// Ignore empty values. a heartrate of 0 would be a bad sign.
|
||||
QPolygonF poly;
|
||||
for (int i = 0, modelDataCount = dataModel->rowCount(); i < modelDataCount; i++) {
|
||||
int hr = dataModel->index(i, vDataColumn).data().toInt();
|
||||
if (!hr)
|
||||
continue;
|
||||
sec = dataModel->index(i, hDataColumn).data().toInt();
|
||||
QPointF point(hAxis->posAtValue(sec), vAxis->posAtValue(hr));
|
||||
poly.append(point);
|
||||
}
|
||||
setPolygon(poly);
|
||||
|
||||
if (texts.count())
|
||||
texts.last()->setAlignment(Qt::AlignLeft | Qt::AlignBottom);
|
||||
}
|
||||
|
||||
void DiveGFLineItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||
{
|
||||
if (polygon().isEmpty())
|
||||
return;
|
||||
painter->save();
|
||||
painter->setPen(pen());
|
||||
painter->drawPolyline(polygon());
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
void DiveGFLineItem::settingsChanged()
|
||||
{
|
||||
setVisible(prefs.percentagegraph);
|
||||
}
|
||||
|
||||
DiveTemperatureItem::DiveTemperatureItem()
|
||||
{
|
||||
QPen pen;
|
||||
|
|
|
@ -107,6 +107,39 @@ private:
|
|||
QString visibilityKey;
|
||||
};
|
||||
|
||||
class DivePercentageItem : public AbstractProfilePolygonItem {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DivePercentageItem(int i);
|
||||
virtual void modelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
|
||||
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
|
||||
virtual void settingsChanged();
|
||||
private:
|
||||
QString visibilityKey;
|
||||
};
|
||||
|
||||
class DiveAmbPressureItem : public AbstractProfilePolygonItem {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DiveAmbPressureItem();
|
||||
virtual void modelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
|
||||
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
|
||||
virtual void settingsChanged();
|
||||
private:
|
||||
QString visibilityKey;
|
||||
};
|
||||
|
||||
class DiveGFLineItem : public AbstractProfilePolygonItem {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DiveGFLineItem();
|
||||
virtual void modelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
|
||||
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
|
||||
virtual void settingsChanged();
|
||||
private:
|
||||
QString visibilityKey;
|
||||
};
|
||||
|
||||
class DiveGasPressureItem : public AbstractProfilePolygonItem {
|
||||
Q_OBJECT
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ static struct _ItemPos {
|
|||
_Axis depth;
|
||||
_Axis partialPressure;
|
||||
_Axis partialPressureWithTankBar;
|
||||
_Axis percentage;
|
||||
_Axis time;
|
||||
_Axis cylinder;
|
||||
_Axis temperature;
|
||||
|
@ -89,6 +90,9 @@ ProfileWidget2::ProfileWidget2(QWidget *parent) : QGraphicsView(parent),
|
|||
po2GasItem(new PartialPressureGasItem()),
|
||||
heartBeatAxis(new DiveCartesianAxis()),
|
||||
heartBeatItem(new DiveHeartrateItem()),
|
||||
percentageAxis(new DiveCartesianAxis()),
|
||||
ambPressureItem(new DiveAmbPressureItem()),
|
||||
gflineItem(new DiveGFLineItem()),
|
||||
mouseFollowerVertical(new DiveLineItem()),
|
||||
mouseFollowerHorizontal(new DiveLineItem()),
|
||||
rulerItem(new RulerItem2()),
|
||||
|
@ -159,6 +163,7 @@ void ProfileWidget2::addItemsToScene()
|
|||
scene()->addItem(pn2GasItem);
|
||||
scene()->addItem(pheGasItem);
|
||||
scene()->addItem(po2GasItem);
|
||||
scene()->addItem(percentageAxis);
|
||||
scene()->addItem(heartBeatAxis);
|
||||
scene()->addItem(heartBeatItem);
|
||||
scene()->addItem(rulerItem);
|
||||
|
@ -174,6 +179,11 @@ void ProfileWidget2::addItemsToScene()
|
|||
Q_FOREACH (DiveCalculatedTissue *tissue, allTissues) {
|
||||
scene()->addItem(tissue);
|
||||
}
|
||||
Q_FOREACH (DivePercentageItem *percentage, allPercentages) {
|
||||
scene()->addItem(percentage);
|
||||
}
|
||||
scene()->addItem(ambPressureItem);
|
||||
scene()->addItem(gflineItem);
|
||||
}
|
||||
|
||||
void ProfileWidget2::setupItemOnScene()
|
||||
|
@ -207,6 +217,12 @@ void ProfileWidget2::setupItemOnScene()
|
|||
heartBeatAxis->setFontLabelScale(0.7);
|
||||
heartBeatAxis->setLineSize(96);
|
||||
|
||||
percentageAxis->setOrientation(DiveCartesianAxis::BottomToTop);
|
||||
percentageAxis->setTickSize(0.2);
|
||||
percentageAxis->setTickInterval(10);
|
||||
percentageAxis->setFontLabelScale(0.7);
|
||||
percentageAxis->setLineSize(96);
|
||||
|
||||
temperatureAxis->setOrientation(DiveCartesianAxis::BottomToTop);
|
||||
temperatureAxis->setTickSize(2);
|
||||
temperatureAxis->setTickInterval(300);
|
||||
|
@ -233,10 +249,15 @@ void ProfileWidget2::setupItemOnScene()
|
|||
DiveCalculatedTissue *tissueItem = new DiveCalculatedTissue();
|
||||
setupItem(tissueItem, timeAxis, profileYAxis, dataModel, DivePlotDataModel::TISSUE_1 + i, DivePlotDataModel::TIME, 1 + i);
|
||||
allTissues.append(tissueItem);
|
||||
DivePercentageItem *percentageItem = new DivePercentageItem(i);
|
||||
setupItem(percentageItem, timeAxis, percentageAxis, dataModel, DivePlotDataModel::PERCENTAGE_1 + i, DivePlotDataModel::TIME, 1 + i);
|
||||
allPercentages.append(percentageItem);
|
||||
}
|
||||
setupItem(gasPressureItem, timeAxis, cylinderPressureAxis, dataModel, DivePlotDataModel::TEMPERATURE, DivePlotDataModel::TIME, 1);
|
||||
setupItem(temperatureItem, timeAxis, temperatureAxis, dataModel, DivePlotDataModel::TEMPERATURE, DivePlotDataModel::TIME, 1);
|
||||
setupItem(heartBeatItem, timeAxis, heartBeatAxis, dataModel, DivePlotDataModel::HEARTBEAT, DivePlotDataModel::TIME, 1);
|
||||
setupItem(ambPressureItem, timeAxis, percentageAxis, dataModel, DivePlotDataModel::AMBPRESSURE, DivePlotDataModel::TIME, 1);
|
||||
setupItem(gflineItem, timeAxis, percentageAxis, dataModel, DivePlotDataModel::GFLINE, DivePlotDataModel::TIME, 1);
|
||||
setupItem(diveProfileItem, timeAxis, profileYAxis, dataModel, DivePlotDataModel::DEPTH, DivePlotDataModel::TIME, 0);
|
||||
|
||||
#define CREATE_PP_GAS(ITEM, VERTICAL_COLUMN, COLOR, COLOR_ALERT, THRESHOULD_SETTINGS, VISIBILITY_SETTINGS) \
|
||||
|
@ -261,6 +282,8 @@ void ProfileWidget2::setupItemOnScene()
|
|||
gasYAxis->setZValue(timeAxis->zValue() + 1);
|
||||
heartBeatAxis->setTextVisible(true);
|
||||
heartBeatAxis->setLinesVisible(true);
|
||||
percentageAxis->setTextVisible(true);
|
||||
percentageAxis->setLinesVisible(true);
|
||||
}
|
||||
|
||||
void ProfileWidget2::replot()
|
||||
|
@ -307,7 +330,7 @@ void ProfileWidget2::setupItemSizes()
|
|||
itemPos.partialPressure.pos.off.setX(110);
|
||||
itemPos.partialPressure.pos.off.setY(63);
|
||||
itemPos.partialPressure.expanded.setP1(QPointF(0, 0));
|
||||
itemPos.partialPressure.expanded.setP2(QPointF(0, 30));
|
||||
itemPos.partialPressure.expanded.setP2(QPointF(0, 20));
|
||||
itemPos.partialPressureWithTankBar = itemPos.partialPressure;
|
||||
itemPos.partialPressureWithTankBar.expanded.setP2(QPointF(0, 27));
|
||||
|
||||
|
@ -334,7 +357,12 @@ void ProfileWidget2::setupItemSizes()
|
|||
itemPos.heartBeat.pos.on.setX(3);
|
||||
itemPos.heartBeat.pos.on.setY(60);
|
||||
itemPos.heartBeat.expanded.setP1(QPointF(0, 0));
|
||||
itemPos.heartBeat.expanded.setP2(QPointF(0, 20));
|
||||
itemPos.heartBeat.expanded.setP2(QPointF(0, 15));
|
||||
|
||||
itemPos.percentage.pos.on.setX(3);
|
||||
itemPos.percentage.pos.on.setY(85); // was 80
|
||||
itemPos.percentage.expanded.setP1(QPointF(0, 0));
|
||||
itemPos.percentage.expanded.setP2(QPointF(0, 15)); // was 20
|
||||
|
||||
itemPos.dcLabel.on.setX(3);
|
||||
itemPos.dcLabel.on.setY(100);
|
||||
|
@ -485,6 +513,11 @@ void ProfileWidget2::plotDive(struct dive *d, bool force)
|
|||
}
|
||||
heartBeatAxis->setVisible(prefs.hrgraph && pInfo.maxhr);
|
||||
|
||||
percentageAxis->setMinimum(0);
|
||||
percentageAxis->setMaximum(100);
|
||||
percentageAxis->setVisible(false);
|
||||
percentageAxis->updateTicks(HR_AXIS);
|
||||
|
||||
timeAxis->setMaximum(maxtime);
|
||||
int i, incr;
|
||||
static int increments[8] = { 10, 20, 30, 60, 5 * 60, 10 * 60, 15 * 60, 30 * 60 };
|
||||
|
@ -760,12 +793,15 @@ void ProfileWidget2::setEmptyState()
|
|||
pn2GasItem->setVisible(false);
|
||||
po2GasItem->setVisible(false);
|
||||
pheGasItem->setVisible(false);
|
||||
ambPressureItem->setVisible(false);
|
||||
gflineItem->setVisible(false);
|
||||
mouseFollowerHorizontal->setVisible(false);
|
||||
mouseFollowerVertical->setVisible(false);
|
||||
|
||||
#define HIDE_ALL(TYPE, CONTAINER) \
|
||||
Q_FOREACH (TYPE *item, CONTAINER) item->setVisible(false);
|
||||
HIDE_ALL(DiveCalculatedTissue, allTissues);
|
||||
HIDE_ALL(DivePercentageItem, allPercentages);
|
||||
HIDE_ALL(DiveEventItem, eventItems);
|
||||
HIDE_ALL(DiveHandler, handles);
|
||||
HIDE_ALL(QGraphicsSimpleTextItem, gases);
|
||||
|
@ -842,6 +878,16 @@ void ProfileWidget2::setProfileState()
|
|||
tissue->setVisible(true);
|
||||
}
|
||||
}
|
||||
percentageAxis->setPos(itemPos.percentage.pos.on);
|
||||
percentageAxis->setLine(itemPos.percentage.expanded);
|
||||
if (prefs.percentagegraph) {
|
||||
Q_FOREACH (DivePercentageItem *percentage, allPercentages) {
|
||||
percentage->setVisible(true);
|
||||
}
|
||||
|
||||
ambPressureItem->setVisible(true);
|
||||
} gflineItem->setVisible(true);
|
||||
|
||||
rulerItem->setVisible(prefs.rulergraph);
|
||||
tankItem->setVisible(prefs.tankbar);
|
||||
tankItem->setPos(itemPos.tankBar.on);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
// */
|
||||
#include "graphicsview-common.h"
|
||||
#include "divelineitem.h"
|
||||
#include "diveprofileitem.h"
|
||||
|
||||
class RulerItem2;
|
||||
struct dive;
|
||||
|
@ -34,6 +35,7 @@ class DiveProfileItem;
|
|||
class TimeAxis;
|
||||
class DiveTemperatureItem;
|
||||
class DiveHeartrateItem;
|
||||
class PercentageItem;
|
||||
class DiveGasPressureItem;
|
||||
class DiveCalculatedCeiling;
|
||||
class DiveCalculatedTissue;
|
||||
|
@ -163,6 +165,10 @@ private:
|
|||
PartialPressureGasItem *po2GasItem;
|
||||
DiveCartesianAxis *heartBeatAxis;
|
||||
DiveHeartrateItem *heartBeatItem;
|
||||
DiveCartesianAxis *percentageAxis;
|
||||
QList<DivePercentageItem *> allPercentages;
|
||||
DiveAmbPressureItem *ambPressureItem;
|
||||
DiveGFLineItem *gflineItem;
|
||||
DiveLineItem *mouseFollowerVertical;
|
||||
DiveLineItem *mouseFollowerHorizontal;
|
||||
RulerItem2 *rulerItem;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue