diff --git a/profile-widget/diveprofileitem.cpp b/profile-widget/diveprofileitem.cpp index a551e2672..73192125f 100644 --- a/profile-widget/diveprofileitem.cpp +++ b/profile-widget/diveprofileitem.cpp @@ -13,8 +13,9 @@ #include "profile-widget/profilewidget2.h" AbstractProfilePolygonItem::AbstractProfilePolygonItem(const DivePlotDataModel &model, const DiveCartesianAxis &horizontal, - const DiveCartesianAxis &vertical, int vColumn, double dpr) : - hAxis(horizontal), vAxis(vertical), dataModel(model), vDataColumn(vColumn), dpr(dpr), from(0), to(0) + const DiveCartesianAxis &vertical, DataAccessor accessor, + double dpr) : + hAxis(horizontal), vAxis(vertical), dataModel(model), accessor(accessor), dpr(dpr), from(0), to(0) { setCacheMode(DeviceCoordinateCache); } @@ -46,18 +47,19 @@ void AbstractProfilePolygonItem::clipStop(double &x, double &y, double prev_x, d std::pair AbstractProfilePolygonItem::getPoint(int i) const { - double x = dataModel.index(i, DivePlotDataModel::TIME).data().toReal(); - double y = dataModel.index(i, vDataColumn).data().toReal(); + const struct plot_data *data = dataModel.data().entry; + double x = data[i].sec; + double y = accessor(data[i]); // Do clipping of first and last value if (i == from && i < to) { - double next_x = dataModel.index(i+1, DivePlotDataModel::TIME).data().toReal(); - double next_y = dataModel.index(i+1, vDataColumn).data().toReal(); + double next_x = data[i+1].sec; + double next_y = accessor(data[i+1]); clipStart(x, y, next_x, next_y); } if (i == to - 1 && i > 0) { - double prev_x = dataModel.index(i-1, DivePlotDataModel::TIME).data().toReal(); - double prev_y = dataModel.index(i-1, vDataColumn).data().toReal(); + double prev_x = data[i-1].sec; + double prev_y = accessor(data[i-1]); clipStop(x, y, prev_x, prev_y); } @@ -96,8 +98,8 @@ void AbstractProfilePolygonItem::makePolygon(int fromIn, int toIn) } DiveProfileItem::DiveProfileItem(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, - const DiveCartesianAxis &vAxis, int vColumn, double dpr) : - AbstractProfilePolygonItem(model, hAxis, vAxis, vColumn, dpr), + const DiveCartesianAxis &vAxis, DataAccessor accessor, double dpr) : + AbstractProfilePolygonItem(model, hAxis, vAxis, accessor, dpr), show_reported_ceiling(0), reported_ceiling_in_red(0) { } @@ -119,10 +121,11 @@ void DiveProfileItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *o pen.setCosmetic(true); pen.setWidth(2); QPolygonF poly = polygon(); + const struct plot_data *data = dataModel.data().entry; // This paints the colors of the velocities. for (int i = from + 1; i < to; i++) { - QModelIndex colorIndex = dataModel.index(i, DivePlotDataModel::COLOR); - pen.setBrush(QBrush(colorIndex.data(Qt::BackgroundRole).value())); + QColor color = getColor((color_index_t)(VELOCITY_COLORS_START_IDX + data[i].velocity)); + pen.setBrush(QBrush(color)); painter->setPen(pen); if (i - from < poly.count() - 1) painter->drawLine(poly[i - from], poly[i - from + 1]); @@ -259,8 +262,8 @@ void DiveProfileItem::plot_depth_sample(const struct plot_data &entry, QFlags texts; @@ -47,7 +49,8 @@ protected: class DiveProfileItem : public AbstractProfilePolygonItem { public: - DiveProfileItem(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, const DiveCartesianAxis &vAxis, int vColumn, double dpr); + DiveProfileItem(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, const DiveCartesianAxis &vAxis, + DataAccessor accessor, double dpr); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) override; void replot(const dive *d, int from, int to, bool in_planner) override; void plot_depth_sample(const struct plot_data &entry, QFlags flags, const QColor &color); @@ -61,7 +64,8 @@ private: class DiveMeanDepthItem : public AbstractProfilePolygonItem { public: - DiveMeanDepthItem(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, const DiveCartesianAxis &vAxis, int vColumn, double dpr); + DiveMeanDepthItem(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, const DiveCartesianAxis &vAxis, + DataAccessor accessor, double dpr); void replot(const dive *d, int from, int to, bool in_planner) override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) override; double labelWidth; @@ -74,7 +78,8 @@ private: class DiveTemperatureItem : public AbstractProfilePolygonItem { public: - DiveTemperatureItem(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, const DiveCartesianAxis &vAxis, int vColumn, double dpr); + DiveTemperatureItem(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, const DiveCartesianAxis &vAxis, + DataAccessor accessor, double dpr); void replot(const dive *d, int from, int to, bool in_planner) override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) override; @@ -84,7 +89,8 @@ private: class DiveHeartrateItem : public AbstractProfilePolygonItem { public: - DiveHeartrateItem(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, const DiveCartesianAxis &vAxis, int vColumn, double dpr); + DiveHeartrateItem(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, const DiveCartesianAxis &vAxis, + DataAccessor accessor, double dpr); void replot(const dive *d, int from, int to, bool in_planner) override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; @@ -120,14 +126,14 @@ private: class DiveCalculatedCeiling : public AbstractProfilePolygonItem { public: DiveCalculatedCeiling(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, - const DiveCartesianAxis &vAxis, int vColumn, double dpr); + const DiveCartesianAxis &vAxis, DataAccessor accessor, double dpr); void replot(const dive *d, int from, int to, bool in_planner) override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) override; }; class DiveReportedCeiling : public AbstractProfilePolygonItem { public: - DiveReportedCeiling(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, const DiveCartesianAxis &vAxis, int vColumn, double dpr); + DiveReportedCeiling(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, const DiveCartesianAxis &vAxis, DataAccessor accessor, double dpr); void replot(const dive *d, int from, int to, bool in_planner) override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) override; private: @@ -138,12 +144,12 @@ private: class DiveCalculatedTissue : public DiveCalculatedCeiling { public: DiveCalculatedTissue(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, - const DiveCartesianAxis &vAxis, int vColumn, double dpr); + const DiveCartesianAxis &vAxis, DataAccessor accessor, double dpr); }; class PartialPressureGasItem : public AbstractProfilePolygonItem { public: - PartialPressureGasItem(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, const DiveCartesianAxis &vAxis, int vColumn, double dpr); + PartialPressureGasItem(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, const DiveCartesianAxis &vAxis, DataAccessor accessor, double dpr); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) override; void replot(const dive *d, int from, int to, bool in_planner) override; void setThresholdSettingsKey(const double *prefPointerMin, const double *prefPointerMax); diff --git a/profile-widget/profilescene.cpp b/profile-widget/profilescene.cpp index e1ecf9c76..fb5eac5da 100644 --- a/profile-widget/profilescene.cpp +++ b/profile-widget/profilescene.cpp @@ -48,23 +48,42 @@ public: }; template -T *ProfileScene::createItem(const DiveCartesianAxis &vAxis, int vColumn, int z, Args&&... args) +T *ProfileScene::createItem(const DiveCartesianAxis &vAxis, DataAccessor accessor, int z, Args&&... args) { - T *res = new T(*dataModel, *timeAxis, vAxis, vColumn, std::forward(args)...); + T *res = new T(*dataModel, *timeAxis, vAxis, accessor, std::forward(args)...); res->setZValue(static_cast(z)); profileItems.push_back(res); return res; } -PartialPressureGasItem *ProfileScene::createPPGas(int column, color_index_t color, color_index_t colorAlert, - const double *thresholdSettingsMin, const double *thresholdSettingsMax) +PartialPressureGasItem *ProfileScene::createPPGas(DataAccessor accessor, color_index_t color, color_index_t colorAlert, + const double *thresholdSettingsMin, const double *thresholdSettingsMax) { - PartialPressureGasItem *item = createItem(*gasYAxis, column, 99, dpr); + PartialPressureGasItem *item = createItem(*gasYAxis, accessor, 99, dpr); item->setThresholdSettingsKey(thresholdSettingsMin, thresholdSettingsMax); item->setColors(getColor(color, isGrayscale), getColor(colorAlert, isGrayscale)); return item; } +template +double accessTissue(const plot_data &item) +{ + return item.ceilings[IDX]; +} + +// For now, the accessor functions for the profile data do not possess a payload. +// To generate the 16 tissue (ceiling) accessor functions, use iterative templates. +// Thanks to C++17's constexpr if, this is actually easy to read and follow. +template +void ProfileScene::addTissueItems(double dpr) +{ + if constexpr (ACT < MAX) { + DiveCalculatedTissue *tissueItem = createItem(*profileYAxis, &accessTissue, ACT + 1, dpr); + allTissues.push_back(tissueItem); + addTissueItems(dpr); + } +} + ProfileScene::ProfileScene(double dpr, bool printMode, bool isGrayscale) : d(nullptr), dc(-1), @@ -88,23 +107,45 @@ ProfileScene::ProfileScene(double dpr, bool printMode, bool isGrayscale) : dpr, 0.7, printMode, isGrayscale, *this)), percentageAxis(new DiveCartesianAxis(DiveCartesianAxis::Position::Right, false, 2, 0, TIME_GRID, Qt::black, false, false, dpr, 0.7, printMode, isGrayscale, *this)), - diveProfileItem(createItem(*profileYAxis, DivePlotDataModel::DEPTH, 0, dpr)), - temperatureItem(createItem(*temperatureAxis, DivePlotDataModel::TEMPERATURE, 1, dpr)), - meanDepthItem(createItem(*profileYAxis, DivePlotDataModel::INSTANT_MEANDEPTH, 1, dpr)), - gasPressureItem(createItem(*cylinderPressureAxis, DivePlotDataModel::TEMPERATURE, 1, dpr)), + diveProfileItem(createItem(*profileYAxis, + [](const plot_data &item) { return (double)item.depth; }, + 0, dpr)), + temperatureItem(createItem(*temperatureAxis, + [](const plot_data &item) { return (double)item.temperature; }, + 1, dpr)), + meanDepthItem(createItem(*profileYAxis, + [](const plot_data &item) { return (double)item.running_sum; }, + 1, dpr)), + gasPressureItem(createItem(*cylinderPressureAxis, + [](const plot_data &item) { return 0.0; }, // unused + 1, dpr)), diveComputerText(new DiveTextItem(dpr, 1.0, Qt::AlignRight | Qt::AlignTop, nullptr)), - reportedCeiling(createItem(*profileYAxis, DivePlotDataModel::CEILING, 1, dpr)), - pn2GasItem(createPPGas(DivePlotDataModel::PN2, PN2, PN2_ALERT, NULL, &prefs.pp_graphs.pn2_threshold)), - pheGasItem(createPPGas(DivePlotDataModel::PHE, PHE, PHE_ALERT, NULL, &prefs.pp_graphs.phe_threshold)), - po2GasItem(createPPGas(DivePlotDataModel::PO2, PO2, PO2_ALERT, &prefs.pp_graphs.po2_threshold_min, &prefs.pp_graphs.po2_threshold_max)), - o2SetpointGasItem(createPPGas(DivePlotDataModel::O2SETPOINT, O2SETPOINT, PO2_ALERT, &prefs.pp_graphs.po2_threshold_min, &prefs.pp_graphs.po2_threshold_max)), - ccrsensor1GasItem(createPPGas(DivePlotDataModel::CCRSENSOR1, CCRSENSOR1, PO2_ALERT, &prefs.pp_graphs.po2_threshold_min, &prefs.pp_graphs.po2_threshold_max)), - ccrsensor2GasItem(createPPGas(DivePlotDataModel::CCRSENSOR2, CCRSENSOR2, PO2_ALERT, &prefs.pp_graphs.po2_threshold_min, &prefs.pp_graphs.po2_threshold_max)), - ccrsensor3GasItem(createPPGas(DivePlotDataModel::CCRSENSOR3, CCRSENSOR3, PO2_ALERT, &prefs.pp_graphs.po2_threshold_min, &prefs.pp_graphs.po2_threshold_max)), - ocpo2GasItem(createPPGas(DivePlotDataModel::SCR_OC_PO2, SCR_OCPO2, PO2_ALERT, &prefs.pp_graphs.po2_threshold_min, &prefs.pp_graphs.po2_threshold_max)), - diveCeiling(createItem(*profileYAxis, DivePlotDataModel::CEILING, 1, dpr)), + reportedCeiling(createItem(*profileYAxis, + [](const plot_data &item) { return (double)item.ceiling; }, + 1, dpr)), + pn2GasItem(createPPGas([](const plot_data &item) { return (double)item.pressures.n2; }, + PN2, PN2_ALERT, NULL, &prefs.pp_graphs.pn2_threshold)), + pheGasItem(createPPGas([](const plot_data &item) { return (double)item.pressures.he; }, + PHE, PHE_ALERT, NULL, &prefs.pp_graphs.phe_threshold)), + po2GasItem(createPPGas([](const plot_data &item) { return (double)item.pressures.o2; }, + PO2, PO2_ALERT, &prefs.pp_graphs.po2_threshold_min, &prefs.pp_graphs.po2_threshold_max)), + o2SetpointGasItem(createPPGas([](const plot_data &item) { return item.o2setpoint.mbar / 1000.0; }, + O2SETPOINT, PO2_ALERT, &prefs.pp_graphs.po2_threshold_min, &prefs.pp_graphs.po2_threshold_max)), + ccrsensor1GasItem(createPPGas([](const plot_data &item) { return item.o2sensor[0].mbar / 1000.0; }, + CCRSENSOR1, PO2_ALERT, &prefs.pp_graphs.po2_threshold_min, &prefs.pp_graphs.po2_threshold_max)), + ccrsensor2GasItem(createPPGas([](const plot_data &item) { return item.o2sensor[1].mbar / 1000.0; }, + CCRSENSOR2, PO2_ALERT, &prefs.pp_graphs.po2_threshold_min, &prefs.pp_graphs.po2_threshold_max)), + ccrsensor3GasItem(createPPGas([](const plot_data &item) { return item.o2sensor[2].mbar / 1000.0; }, + CCRSENSOR3, PO2_ALERT, &prefs.pp_graphs.po2_threshold_min, &prefs.pp_graphs.po2_threshold_max)), + ocpo2GasItem(createPPGas([](const plot_data &item) { return item.scr_OC_pO2.mbar / 1000.0; }, + SCR_OCPO2, PO2_ALERT, &prefs.pp_graphs.po2_threshold_min, &prefs.pp_graphs.po2_threshold_max)), + diveCeiling(createItem(*profileYAxis, + [](const plot_data &item) { return (double)item.ceiling; }, + 1, dpr)), decoModelParameters(new DiveTextItem(dpr, 1.0, Qt::AlignHCenter | Qt::AlignTop, nullptr)), - heartBeatItem(createItem(*heartBeatAxis, DivePlotDataModel::HEARTBEAT, 1, dpr)), + heartBeatItem(createItem(*heartBeatAxis, + [](const plot_data &item) { return (double)item.heartbeat; }, + 1, dpr)), percentageItem(new DivePercentageItem(*timeAxis, *percentageAxis, dpr)), tankItem(new TankItem(*timeAxis, dpr)), pixmaps(getDivePixmaps(dpr)) @@ -122,10 +163,7 @@ ProfileScene::ProfileScene(double dpr, bool printMode, bool isGrayscale) : heartBeatAxis->setTransform(1.0); gasYAxis->setTransform(1.0); // Non-metric countries likewise use bar (disguised as "percentage") for partial pressure. - for (int i = 0; i < 16; i++) { - DiveCalculatedTissue *tissueItem = createItem(*profileYAxis, DivePlotDataModel::TISSUE_1 + i, i + 1, dpr); - allTissues.append(tissueItem); - } + addTissueItems<0,16>(dpr); percentageItem->setZValue(1.0); diff --git a/profile-widget/profilescene.h b/profile-widget/profilescene.h index d79e24ec0..c512c4509 100644 --- a/profile-widget/profilescene.h +++ b/profile-widget/profilescene.h @@ -54,9 +54,11 @@ public: const struct dive *d; int dc; private: - template T *createItem(const DiveCartesianAxis &vAxis, int vColumn, int z, Args&&... args); - PartialPressureGasItem *createPPGas(int column, color_index_t color, color_index_t colorAlert, + using DataAccessor = double (*)(const plot_data &data); + template T *createItem(const DiveCartesianAxis &vAxis, DataAccessor accessor, int z, Args&&... args); + PartialPressureGasItem *createPPGas(DataAccessor accessor, color_index_t color, color_index_t colorAlert, const double *thresholdSettingsMin, const double *thresholdSettingsMax); + template void addTissueItems(double dpr); void updateVisibility(bool diveHasHeartBeat, bool simplified); // Update visibility of non-interactive chart features according to preferences void updateAxes(bool diveHasHeartBeat, bool simplified); // Update axes according to preferences @@ -94,7 +96,7 @@ private: PartialPressureGasItem *ocpo2GasItem; DiveCalculatedCeiling *diveCeiling; DiveTextItem *decoModelParameters; - QList allTissues; + std::vector allTissues; DiveHeartrateItem *heartBeatItem; DivePercentageItem *percentageItem; TankItem *tankItem;