mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-19 22:16:15 +00:00
profile: start with "absolutizing" the profile layout
The chart items were laid out in relative terms with respect to a fixed scene size (100.0, 100.0). This simply does not cut it when resizing the chart. Why should the axes always occupy the same fraction of the chart independent of the size. Moreover, this led to basically unmaintainable code. Resize the scene according to the viewport and do absolute placement of the items. This breaks the layout, but at least now we have a chance to fix things somewhat reasonably. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
parent
99284a88f7
commit
d0beae59f9
7 changed files with 174 additions and 272 deletions
|
@ -15,32 +15,7 @@
|
|||
#include "qt-models/diveplotdatamodel.h"
|
||||
#include "qt-models/diveplannermodel.h"
|
||||
|
||||
const static struct ProfileItemPos {
|
||||
struct Pos {
|
||||
QPointF on;
|
||||
QPointF off;
|
||||
};
|
||||
struct Axis : public Pos {
|
||||
QLineF shrinked;
|
||||
QLineF expanded;
|
||||
QLineF intermediate;
|
||||
};
|
||||
Pos dcLabel;
|
||||
Pos tankBar;
|
||||
Axis depth;
|
||||
Axis partialPressure;
|
||||
Axis partialPressureTissue;
|
||||
Axis partialPressureWithTankBar;
|
||||
Axis percentage;
|
||||
Axis percentageWithTankBar;
|
||||
Axis time;
|
||||
Axis cylinder;
|
||||
Axis temperature;
|
||||
Axis temperatureAll;
|
||||
Axis heartBeat;
|
||||
Axis heartBeatWithTankBar;
|
||||
ProfileItemPos();
|
||||
} itemPos;
|
||||
static const double diveComputerTextBorder = 1.0;
|
||||
|
||||
template<typename T, class... Args>
|
||||
T *ProfileScene::createItem(const DiveCartesianAxis &vAxis, int vColumn, int z, Args&&... args)
|
||||
|
@ -92,7 +67,7 @@ ProfileScene::ProfileScene(double dpr, bool printMode, bool isGrayscale) :
|
|||
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<DiveCalculatedCeiling>(*profileYAxis, DivePlotDataModel::CEILING, 1, dpr)),
|
||||
decoModelParameters(new DiveTextItem(dpr, 1.0, Qt::AlignHCenter | Qt::AlignBottom, nullptr)),
|
||||
decoModelParameters(new DiveTextItem(dpr, 1.0, Qt::AlignHCenter | Qt::AlignTop, nullptr)),
|
||||
heartBeatItem(createItem<DiveHeartrateItem>(*heartBeatAxis, DivePlotDataModel::HEARTBEAT, 1, dpr)),
|
||||
tankItem(new TankItem(*timeAxis, dpr))
|
||||
{
|
||||
|
@ -106,9 +81,7 @@ ProfileScene::ProfileScene(double dpr, bool printMode, bool isGrayscale) :
|
|||
profileYAxis->setMinimum(0);
|
||||
profileYAxis->setTickInterval(M_OR_FT(10, 30));
|
||||
profileYAxis->setTickSize(0.5);
|
||||
profileYAxis->setLineSize(96);
|
||||
|
||||
timeAxis->setLineSize(92);
|
||||
timeAxis->setTickSize(-0.5);
|
||||
|
||||
gasYAxis->setOrientation(DiveCartesianAxis::BottomToTop);
|
||||
|
@ -116,20 +89,17 @@ ProfileScene::ProfileScene(double dpr, bool printMode, bool isGrayscale) :
|
|||
gasYAxis->setTickSize(1);
|
||||
gasYAxis->setMinimum(0);
|
||||
gasYAxis->setFontLabelScale(0.7);
|
||||
gasYAxis->setLineSize(96);
|
||||
|
||||
#ifndef SUBSURFACE_MOBILE
|
||||
heartBeatAxis->setOrientation(DiveCartesianAxis::BottomToTop);
|
||||
heartBeatAxis->setTickSize(0.2);
|
||||
heartBeatAxis->setTickInterval(10);
|
||||
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);
|
||||
#endif
|
||||
|
||||
temperatureAxis->setOrientation(DiveCartesianAxis::BottomToTop);
|
||||
|
@ -154,14 +124,6 @@ ProfileScene::ProfileScene(double dpr, bool printMode, bool isGrayscale) :
|
|||
gasYAxis->setZValue(timeAxis->zValue() + 1);
|
||||
tankItem->setZValue(100);
|
||||
|
||||
// show the deco model parameters at the top in the center
|
||||
decoModelParameters->setY(0);
|
||||
decoModelParameters->setX(50);
|
||||
|
||||
diveComputerText->setPos(itemPos.dcLabel.on);
|
||||
|
||||
tankItem->setPos(itemPos.tankBar.on);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
DiveCalculatedTissue *tissueItem = createItem<DiveCalculatedTissue>(*profileYAxis, DivePlotDataModel::TISSUE_1 + i, i + 1, dpr);
|
||||
allTissues.append(tissueItem);
|
||||
|
@ -231,7 +193,7 @@ void ProfileScene::updateVisibility()
|
|||
heartBeatItem->setVisible(prefs.hrgraph);
|
||||
#endif
|
||||
diveCeiling->setVisible(prefs.calcceiling);
|
||||
decoModelParameters->setVisible(prefs.calcceiling);
|
||||
decoModelParameters->setVisible(prefs.decoinfo);
|
||||
#ifndef SUBSURFACE_MOBILE
|
||||
for (DiveCalculatedTissue *tissue: allTissues)
|
||||
tissue->setVisible(prefs.calcalltissues && prefs.calcceiling);
|
||||
|
@ -243,71 +205,99 @@ void ProfileScene::updateVisibility()
|
|||
tankItem->setVisible(prefs.tankbar);
|
||||
}
|
||||
|
||||
void ProfileScene::resize(QSizeF size)
|
||||
{
|
||||
setSceneRect(QRectF(QPointF(), size));
|
||||
updateAxes(true); // disable animations when resizing
|
||||
}
|
||||
|
||||
// Helper structure for laying out secondary plots.
|
||||
struct VerticalAxisLayout {
|
||||
DiveCartesianAxis *axis;
|
||||
double height;
|
||||
bool visible;
|
||||
};
|
||||
|
||||
void ProfileScene::updateAxes(bool instant)
|
||||
{
|
||||
int animSpeed = instant || printMode ? 0 : qPrefDisplay::animation_speed();
|
||||
|
||||
profileYAxis->setPos(itemPos.depth.on);
|
||||
// Calculate left and right border needed for the axes.
|
||||
// viz. the depth axis to the left and the partial pressure axis to the right.
|
||||
// Thus, calculating the "border" of the graph is trivial.
|
||||
double leftBorder = profileYAxis->width();
|
||||
if (prefs.hrgraph)
|
||||
leftBorder = std::max(leftBorder, heartBeatAxis->width());
|
||||
double rightWidth = ppGraphsEnabled() ? gasYAxis->width() : 0.0;
|
||||
double rightBorder = sceneRect().width() - rightWidth;
|
||||
double width = rightBorder - leftBorder;
|
||||
|
||||
#ifndef SUBSURFACE_MOBILE
|
||||
gasYAxis->update(animSpeed); // Initialize ticks of partial pressure graph
|
||||
if ((prefs.percentagegraph||prefs.hrgraph) && ppGraphsEnabled()) {
|
||||
profileYAxis->animateChangeLine(itemPos.depth.shrinked, animSpeed);
|
||||
temperatureAxis->setPos(itemPos.temperatureAll.on);
|
||||
temperatureAxis->animateChangeLine(itemPos.temperature.shrinked, animSpeed);
|
||||
cylinderPressureAxis->animateChangeLine(itemPos.cylinder.shrinked, animSpeed);
|
||||
if (width <= 10.0 * dpr)
|
||||
return clear();
|
||||
|
||||
if (prefs.tankbar) {
|
||||
percentageAxis->setPos(itemPos.percentageWithTankBar.on);
|
||||
percentageAxis->animateChangeLine(itemPos.percentageWithTankBar.expanded, animSpeed);
|
||||
heartBeatAxis->setPos(itemPos.heartBeatWithTankBar.on);
|
||||
heartBeatAxis->animateChangeLine(itemPos.heartBeatWithTankBar.expanded, animSpeed);
|
||||
} else {
|
||||
percentageAxis->setPos(itemPos.percentage.on);
|
||||
percentageAxis->animateChangeLine(itemPos.percentage.expanded, animSpeed);
|
||||
heartBeatAxis->setPos(itemPos.heartBeat.on);
|
||||
heartBeatAxis->animateChangeLine(itemPos.heartBeat.expanded, animSpeed);
|
||||
}
|
||||
gasYAxis->setPos(itemPos.partialPressureTissue.on);
|
||||
gasYAxis->animateChangeLine(itemPos.partialPressureTissue.expanded, animSpeed);
|
||||
} else if (ppGraphsEnabled() || prefs.hrgraph || prefs.percentagegraph) {
|
||||
profileYAxis->animateChangeLine(itemPos.depth.intermediate, animSpeed);
|
||||
temperatureAxis->setPos(itemPos.temperature.on);
|
||||
temperatureAxis->animateChangeLine(itemPos.temperature.intermediate, animSpeed);
|
||||
cylinderPressureAxis->animateChangeLine(itemPos.cylinder.intermediate, animSpeed);
|
||||
if (prefs.tankbar) {
|
||||
percentageAxis->setPos(itemPos.percentageWithTankBar.on);
|
||||
percentageAxis->animateChangeLine(itemPos.percentageWithTankBar.expanded, animSpeed);
|
||||
gasYAxis->setPos(itemPos.partialPressureWithTankBar.on);
|
||||
gasYAxis->animateChangeLine(itemPos.partialPressureWithTankBar.expanded, animSpeed);
|
||||
heartBeatAxis->setPos(itemPos.heartBeatWithTankBar.on);
|
||||
heartBeatAxis->animateChangeLine(itemPos.heartBeatWithTankBar.expanded, animSpeed);
|
||||
} else {
|
||||
gasYAxis->setPos(itemPos.partialPressure.on);
|
||||
gasYAxis->animateChangeLine(itemPos.partialPressure.expanded, animSpeed);
|
||||
percentageAxis->setPos(itemPos.percentage.on);
|
||||
percentageAxis->animateChangeLine(itemPos.percentage.expanded, animSpeed);
|
||||
heartBeatAxis->setPos(itemPos.heartBeat.on);
|
||||
heartBeatAxis->animateChangeLine(itemPos.heartBeat.expanded, animSpeed);
|
||||
}
|
||||
} else {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
profileYAxis->animateChangeLine(itemPos.depth.expanded, animSpeed);
|
||||
if (prefs.tankbar) {
|
||||
temperatureAxis->setPos(itemPos.temperatureAll.on);
|
||||
} else {
|
||||
temperatureAxis->setPos(itemPos.temperature.on);
|
||||
}
|
||||
temperatureAxis->animateChangeLine(itemPos.temperature.expanded, animSpeed);
|
||||
cylinderPressureAxis->animateChangeLine(itemPos.cylinder.expanded, animSpeed);
|
||||
// Place the fixed dive computer text at the bottom
|
||||
double bottomBorder = sceneRect().height() - diveComputerText->height() - 2.0 * dpr * diveComputerTextBorder;
|
||||
diveComputerText->setPos(0.0, bottomBorder + dpr * diveComputerTextBorder);
|
||||
|
||||
double topBorder = 0.0;
|
||||
|
||||
// show the deco model parameters at the top in the center
|
||||
if (prefs.decoinfo) {
|
||||
decoModelParameters->setPos(leftBorder + width / 2.0, topBorder);
|
||||
topBorder += decoModelParameters->height();
|
||||
}
|
||||
|
||||
timeAxis->setPos(itemPos.time.on);
|
||||
timeAxis->setLine(itemPos.time.expanded);
|
||||
bottomBorder -= timeAxis->height();
|
||||
timeAxis->animateChangeLine(QRectF(leftBorder, topBorder, width, bottomBorder - topBorder), animSpeed);
|
||||
|
||||
cylinderPressureAxis->setPos(itemPos.cylinder.on);
|
||||
if (prefs.tankbar) {
|
||||
bottomBorder -= tankItem->height();
|
||||
// Note: we set x to 0.0, because the tank item uses the timeAxis to set the x-coordinate.
|
||||
tankItem->setPos(0.0, bottomBorder);
|
||||
}
|
||||
|
||||
double height = bottomBorder - topBorder;
|
||||
if (height <= 50.0 * dpr)
|
||||
return clear();
|
||||
|
||||
// The rest is laid out dynamically. Give at least 50% to the actual profile.
|
||||
// The max heights are given for DPR=1, i.e. a ca. 800x600 pixels profile.
|
||||
const double minProfileFraction = 0.5;
|
||||
VerticalAxisLayout secondaryAxes[] = {
|
||||
// Note: axes are listed from bottom to top, since they are added that way.
|
||||
{ heartBeatAxis, 75.0, prefs.hrgraph },
|
||||
{ percentageAxis, 50.0, prefs.percentagegraph },
|
||||
{ gasYAxis, 75.0, ppGraphsEnabled() },
|
||||
{ temperatureAxis, 50.0, true },
|
||||
};
|
||||
|
||||
// A loop is probably easier to read than std::accumulate.
|
||||
double totalSecondaryHeight = 0.0;
|
||||
for (const VerticalAxisLayout &l: secondaryAxes) {
|
||||
if (l.visible)
|
||||
totalSecondaryHeight += l.height * dpr;
|
||||
}
|
||||
|
||||
if (totalSecondaryHeight > height * minProfileFraction) {
|
||||
// Use 50% for the profile and the rest for the remaining graphs, scaled by their maximum height.
|
||||
double remainingSpace = height * minProfileFraction;
|
||||
for (VerticalAxisLayout &l: secondaryAxes)
|
||||
l.height *= remainingSpace / totalSecondaryHeight;
|
||||
}
|
||||
|
||||
for (const VerticalAxisLayout &l: secondaryAxes) {
|
||||
l.axis->setVisible(l.visible);
|
||||
if (!l.visible)
|
||||
continue;
|
||||
bottomBorder -= l.height * dpr;
|
||||
l.axis->animateChangeLine(QRectF(leftBorder, bottomBorder, width, l.height * dpr), animSpeed);
|
||||
}
|
||||
|
||||
height = bottomBorder - topBorder;
|
||||
profileYAxis->animateChangeLine(QRectF(leftBorder, topBorder, width, height), animSpeed);
|
||||
|
||||
// The cylinders are displayed in the 24-80% region of the profile
|
||||
cylinderPressureAxis->animateChangeLine(QRectF(leftBorder, topBorder + 0.24 * height, width, 0.56 * height), animSpeed);
|
||||
}
|
||||
|
||||
bool ProfileScene::isPointOutOfBoundaries(const QPointF &point) const
|
||||
|
@ -320,123 +310,6 @@ bool ProfileScene::isPointOutOfBoundaries(const QPointF &point) const
|
|||
ypos < profileYAxis->minimum();
|
||||
}
|
||||
|
||||
ProfileItemPos::ProfileItemPos()
|
||||
{
|
||||
// Scene is *always* (double) 100 / 100.
|
||||
// TODO: The relative scaling doesn't work well with large or small
|
||||
// profiles and needs to be fixed.
|
||||
|
||||
dcLabel.on.setX(3);
|
||||
dcLabel.on.setY(100);
|
||||
dcLabel.off.setX(-10);
|
||||
dcLabel.off.setY(100);
|
||||
|
||||
tankBar.on.setX(0);
|
||||
#ifndef SUBSURFACE_MOBILE
|
||||
tankBar.on.setY(91.95);
|
||||
#else
|
||||
tankBar.on.setY(86.4);
|
||||
#endif
|
||||
|
||||
//Depth Axis Config
|
||||
depth.on.setX(3);
|
||||
depth.on.setY(3);
|
||||
depth.off.setX(-2);
|
||||
depth.off.setY(3);
|
||||
depth.expanded.setP1(QPointF(0, 0));
|
||||
#ifndef SUBSURFACE_MOBILE
|
||||
depth.expanded.setP2(QPointF(0, 85));
|
||||
#else
|
||||
depth.expanded.setP2(QPointF(0, 65));
|
||||
#endif
|
||||
depth.shrinked.setP1(QPointF(0, 0));
|
||||
depth.shrinked.setP2(QPointF(0, 55));
|
||||
depth.intermediate.setP1(QPointF(0, 0));
|
||||
depth.intermediate.setP2(QPointF(0, 65));
|
||||
|
||||
// Time Axis Config
|
||||
time.on.setX(3);
|
||||
#ifndef SUBSURFACE_MOBILE
|
||||
time.on.setY(95);
|
||||
#else
|
||||
time.on.setY(89.5);
|
||||
#endif
|
||||
time.off.setX(3);
|
||||
time.off.setY(110);
|
||||
time.expanded.setP1(QPointF(0, 0));
|
||||
time.expanded.setP2(QPointF(94, 0));
|
||||
|
||||
// Partial Gas Axis Config
|
||||
partialPressure.on.setX(97);
|
||||
#ifndef SUBSURFACE_MOBILE
|
||||
partialPressure.on.setY(75);
|
||||
#else
|
||||
partialPressure.on.setY(70);
|
||||
#endif
|
||||
partialPressure.off.setX(110);
|
||||
partialPressure.off.setY(63);
|
||||
partialPressure.expanded.setP1(QPointF(0, 0));
|
||||
#ifndef SUBSURFACE_MOBILE
|
||||
partialPressure.expanded.setP2(QPointF(0, 19));
|
||||
#else
|
||||
partialPressure.expanded.setP2(QPointF(0, 20));
|
||||
#endif
|
||||
partialPressureWithTankBar = partialPressure;
|
||||
partialPressureWithTankBar.expanded.setP2(QPointF(0, 17));
|
||||
partialPressureTissue = partialPressure;
|
||||
partialPressureTissue.on.setX(97);
|
||||
partialPressureTissue.on.setY(65);
|
||||
partialPressureTissue.expanded.setP2(QPointF(0, 16));
|
||||
|
||||
// cylinder axis config
|
||||
cylinder.on.setX(3);
|
||||
cylinder.on.setY(20);
|
||||
cylinder.off.setX(-10);
|
||||
cylinder.off.setY(20);
|
||||
cylinder.expanded.setP1(QPointF(0, 15));
|
||||
cylinder.expanded.setP2(QPointF(0, 50));
|
||||
cylinder.shrinked.setP1(QPointF(0, 0));
|
||||
cylinder.shrinked.setP2(QPointF(0, 20));
|
||||
cylinder.intermediate.setP1(QPointF(0, 0));
|
||||
cylinder.intermediate.setP2(QPointF(0, 20));
|
||||
|
||||
// Temperature axis config
|
||||
temperature.on.setX(3);
|
||||
temperature.off.setX(-10);
|
||||
temperature.off.setY(40);
|
||||
temperature.expanded.setP1(QPointF(0, 20));
|
||||
temperature.expanded.setP2(QPointF(0, 33));
|
||||
temperature.shrinked.setP1(QPointF(0, 2));
|
||||
temperature.shrinked.setP2(QPointF(0, 12));
|
||||
#ifndef SUBSURFACE_MOBILE
|
||||
temperature.on.setY(60);
|
||||
temperatureAll.on.setY(51);
|
||||
temperature.intermediate.setP1(QPointF(0, 2));
|
||||
temperature.intermediate.setP2(QPointF(0, 12));
|
||||
#else
|
||||
temperature.on.setY(51);
|
||||
temperatureAll.on.setY(47);
|
||||
temperature.intermediate.setP1(QPointF(0, 2));
|
||||
temperature.intermediate.setP2(QPointF(0, 12));
|
||||
#endif
|
||||
|
||||
// Heart rate axis config
|
||||
heartBeat.on.setX(3);
|
||||
heartBeat.on.setY(82);
|
||||
heartBeat.expanded.setP1(QPointF(0, 0));
|
||||
heartBeat.expanded.setP2(QPointF(0, 10));
|
||||
heartBeatWithTankBar = heartBeat;
|
||||
heartBeatWithTankBar.expanded.setP2(QPointF(0, 7));
|
||||
|
||||
// Percentage axis config
|
||||
percentage.on.setX(3);
|
||||
percentage.on.setY(80);
|
||||
percentage.expanded.setP1(QPointF(0, 0));
|
||||
percentage.expanded.setP2(QPointF(0, 15));
|
||||
percentageWithTankBar = percentage;
|
||||
percentageWithTankBar.expanded.setP2(QPointF(0, 11.9));
|
||||
}
|
||||
|
||||
void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsModel *plannerModel, bool inPlanner, bool instant, bool calcMax)
|
||||
{
|
||||
d = dIn;
|
||||
|
@ -530,7 +403,7 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM
|
|||
heartBeatAxis->setMinimum(heartBeatAxisMin);
|
||||
heartBeatAxis->setMaximum(heartBeatAxisMax + 1);
|
||||
heartBeatAxis->setTickInterval(heartBeatAxisTick);
|
||||
heartBeatAxis->updateTicks(animSpeed);
|
||||
heartBeatAxis->updateTicks(animSpeed); // this shows the ticks
|
||||
}
|
||||
heartBeatAxis->setVisible(prefs.hrgraph && plotInfo.maxhr);
|
||||
|
||||
|
@ -564,9 +437,6 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM
|
|||
|
||||
#ifdef SUBSURFACE_MOBILE
|
||||
if (currentdc->divemode == CCR) {
|
||||
gasYAxis->setPos(itemPos.partialPressure.on);
|
||||
gasYAxis->setLine(itemPos.partialPressure.expanded);
|
||||
|
||||
tankItem->setVisible(false);
|
||||
pn2GasItem->setVisible(false);
|
||||
po2GasItem->setVisible(prefs.pp_graphs.po2);
|
||||
|
@ -588,7 +458,6 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM
|
|||
temperatureItem->setVisible(false);
|
||||
} else {
|
||||
tankItem->setVisible(prefs.tankbar);
|
||||
gasYAxis->setPos(itemPos.partialPressure.off);
|
||||
pn2GasItem->setVisible(false);
|
||||
po2GasItem->setVisible(false);
|
||||
pheGasItem->setVisible(false);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue