diff --git a/profile-widget/divecartesianaxis.cpp b/profile-widget/divecartesianaxis.cpp index 4689b9278..13a54114d 100644 --- a/profile-widget/divecartesianaxis.cpp +++ b/profile-widget/divecartesianaxis.cpp @@ -14,8 +14,8 @@ static const double labelSpaceVertical = 2.0; // space between label and ticks void DiveCartesianAxis::setBounds(double minimum, double maximum) { changed = !IS_FP_SAME(max, maximum) || !IS_FP_SAME(min, minimum); - min = minimum; - max = maximum; + dataMin = min = minimum; + dataMax = max = maximum; } DiveCartesianAxis::DiveCartesianAxis(Position position, int integralDigits, int fractionalDigits, color_index_t gridColor, double dpr, @@ -28,7 +28,6 @@ DiveCartesianAxis::DiveCartesianAxis(Position position, int integralDigits, int orientation(LeftToRight), min(0), max(0), - interval(1), textVisibility(true), lineVisibility(true), labelScale(labelScale), @@ -136,35 +135,59 @@ void DiveCartesianAxis::updateTicks(int animSpeed) { if (!changed && !printMode) return; + + if (dataMax - dataMin < 1e-5) + return; + + // Guess the number of tick marks. QLineF m = line(); - double stepsInRange = (max - min) / interval; - int steps = (int)stepsInRange; + double spaceNeeded = position == Position::Bottom ? labelWidth * 3.0 / 2.0 + : labelHeight * 2.0; + double size = position == Position::Bottom ? fabs(m.x2() - m.x1()) + : fabs(m.y2() - m.y1()); + int numTicks = lrint(size / spaceNeeded); + + numTicks = std::clamp(numTicks, 2, 50); + double interval = (dataMax - dataMin) / numTicks; + + // Round the interval to a sensible size in display units + double intervalDisplay = interval * transform.a; + intervalDisplay = ceil(intervalDisplay); // Currently, round to full integers, might want to improve. + + // Choose full multiples of the interval as minumum and maximum values + double minDisplay = transform.to(dataMin); + double maxDisplay = transform.to(dataMax); + double firstDisplay = floor(minDisplay / intervalDisplay * (1.0 + 1e-5)) * intervalDisplay; + double lastDisplay = ceil(maxDisplay / intervalDisplay * (1.0 - 1e-5)) * intervalDisplay; + numTicks = lrint((lastDisplay - firstDisplay) / intervalDisplay) + 1; + numTicks = std::max(numTicks, 0); + + min = transform.from(firstDisplay); + max = transform.from(lastDisplay); + double currValueText = min; double currValueLine = min; - if (steps < 1) + emptyList(labels, numTicks, animSpeed); + emptyList(lines, numTicks, animSpeed); + if (numTicks == 0) return; - emptyList(labels, steps, animSpeed); - emptyList(lines, steps, animSpeed); + interval = numTicks > 1 ? (max - min) / (numTicks - 1) : 0; + double stepSize = numTicks > 1 ? size / (numTicks - 1) : 0; // Move the remaining grid lines / labels to their correct positions // regarding the possible new values for the axis - qreal begin, stepSize; + double begin; if (orientation == TopToBottom) { begin = m.y1(); - stepSize = (m.y2() - m.y1()); } else if (orientation == BottomToTop) { begin = m.y2(); - stepSize = (m.y2() - m.y1()); } else if (orientation == LeftToRight) { begin = m.x1(); - stepSize = (m.x2() - m.x1()); } else /* if (orientation == RightToLeft) */ { begin = m.x2(); - stepSize = (m.x2() - m.x1()); } - stepSize /= stepsInRange; for (int i = 0, count = labels.size(); i < count; i++, currValueText += interval) { double childPos = (orientation == TopToBottom || orientation == LeftToRight) ? @@ -205,7 +228,7 @@ void DiveCartesianAxis::updateTicks(int animSpeed) } // Add the rest of the needed labels. - for (int i = labels.size(); i < steps; i++, currValueText += interval) { + for (int i = labels.size(); i < numTicks; i++, currValueText += interval) { double childPos; if (orientation == TopToBottom || orientation == LeftToRight) { childPos = begin + i * stepSize; @@ -237,7 +260,7 @@ void DiveCartesianAxis::updateTicks(int animSpeed) } // Add the rest of the needed grid lines. - for (int i = lines.size(); i < steps; i++, currValueText += interval) { + for (int i = lines.size(); i < numTicks; i++, currValueText += interval) { double childPos; if (orientation == TopToBottom || orientation == LeftToRight) { childPos = begin + i * stepSize; @@ -306,11 +329,6 @@ QString DiveCartesianAxis::textForValue(double value) const return QStringLiteral("%L1").arg(transform.to(value), 0, 'f', fractionalDigits); } -void DiveCartesianAxis::setTickInterval(double i) -{ - interval = i; -} - qreal DiveCartesianAxis::valueAt(const QPointF &p) const { double fraction; @@ -390,17 +408,6 @@ QString TimeAxis::textForValue(double value) const return QString::number(nr); } -// TODO: replace by real dynamic axis - this is just weird. -void TimeAxis::updateTicks(int animSpeed) -{ - DiveCartesianAxis::updateTicks(animSpeed); - if (maximum() > 600) { - for (int i = 0; i < labels.count(); i++) { - labels[i]->setVisible(i % 2); - } - } -} - PartialGasPressureAxis::PartialGasPressureAxis(const DivePlotDataModel &model, Position position, int integralDigits, int fractionalDigits, color_index_t gridColor, double dpr, double labelScale, bool printMode, bool isGrayscale, ProfileScene &scene) : @@ -429,6 +436,5 @@ void PartialGasPressureAxis::update(int animSpeed) return; setBounds(0.0, pp); - setTickInterval(pp > 4 ? 0.5 : 0.25); updateTicks(animSpeed); } diff --git a/profile-widget/divecartesianaxis.h b/profile-widget/divecartesianaxis.h index 55cd32ac8..479dc0513 100644 --- a/profile-widget/divecartesianaxis.h +++ b/profile-widget/divecartesianaxis.h @@ -38,7 +38,6 @@ public: ~DiveCartesianAxis(); void setBounds(double min, double max); void setTransform(double a, double b = 0.0); - void setTickInterval(double interval); void setOrientation(Orientation orientation); double minimum() const; double maximum() const; @@ -49,7 +48,7 @@ public: void setTextVisible(bool arg1); void setLinesVisible(bool arg1); void setLine(const QLineF &line); - virtual void updateTicks(int animSpeed); + void updateTicks(int animSpeed); double width() const; // only for vertical axes double height() const; // only for horizontal axes @@ -68,9 +67,8 @@ protected: Orientation orientation; QList labels; QList lines; - double min; - double max; - double interval; + double dataMin, dataMax; + double min, max; bool textVisibility; bool lineVisibility; double labelScale; @@ -101,7 +99,6 @@ class TimeAxis : public DiveCartesianAxis { Q_OBJECT public: using DiveCartesianAxis::DiveCartesianAxis; - void updateTicks(int animSpeed) override; private: QString textForValue(double value) const override; QColor colorForValue(double value) const override; diff --git a/profile-widget/divetextitem.cpp b/profile-widget/divetextitem.cpp index 1b8580d2a..88314ad15 100644 --- a/profile-widget/divetextitem.cpp +++ b/profile-widget/divetextitem.cpp @@ -84,7 +84,7 @@ QFont DiveTextItem::getFont(double dpr, double scale) double DiveTextItem::outlineSpace(double dpr) { - return 2.0 * outlineSize * dpr; // Double because outline growths to both sides. + return outlineSize * dpr; } double DiveTextItem::fontHeight(double dpr, double scale) diff --git a/profile-widget/profilescene.cpp b/profile-widget/profilescene.cpp index c508e61a5..761a6dffc 100644 --- a/profile-widget/profilescene.cpp +++ b/profile-widget/profilescene.cpp @@ -82,24 +82,18 @@ ProfileScene::ProfileScene(double dpr, bool printMode, bool isGrayscale) : // Initialize axes. Perhaps part of this should be moved down to the axes code? profileYAxis->setOrientation(DiveCartesianAxis::TopToBottom); - profileYAxis->setTickInterval(M_OR_FT(10, 30)); gasYAxis->setOrientation(DiveCartesianAxis::BottomToTop); - gasYAxis->setTickInterval(1); #ifndef SUBSURFACE_MOBILE heartBeatAxis->setOrientation(DiveCartesianAxis::BottomToTop); - heartBeatAxis->setTickInterval(10); percentageAxis->setOrientation(DiveCartesianAxis::BottomToTop); - percentageAxis->setTickInterval(10); #endif temperatureAxis->setOrientation(DiveCartesianAxis::BottomToTop); - temperatureAxis->setTickInterval(300); cylinderPressureAxis->setOrientation(DiveCartesianAxis::BottomToTop); - cylinderPressureAxis->setTickInterval(30000); heartBeatAxis->setTextVisible(true); heartBeatAxis->setLinesVisible(true); @@ -116,7 +110,7 @@ ProfileScene::ProfileScene(double dpr, bool printMode, bool isGrayscale) : tankItem->setZValue(100); // These axes are not locale-dependent. Set their scale factor once here. - timeAxis->setTransform(60.0); + timeAxis->setTransform(1.0/60.0); heartBeatAxis->setTransform(1.0); gasYAxis->setTransform(1.0); // Non-metric countries likewise use bar (disguised as "percentage") for partial pressure. @@ -407,20 +401,8 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM plotInfo.maxtemp - plotInfo.mintemp > 2000 ? plotInfo.maxtemp : plotInfo.mintemp + 2000); if (hasHeartBeat) { - int heartBeatAxisMin = lrint(plotInfo.minhr / 5.0 - 0.5) * 5; - int heartBeatAxisMax, heartBeatAxisTick; - if (plotInfo.maxhr - plotInfo.minhr < 40) - heartBeatAxisTick = 10; - else if (plotInfo.maxhr - plotInfo.minhr < 80) - heartBeatAxisTick = 20; - else if (plotInfo.maxhr - plotInfo.minhr < 100) - heartBeatAxisTick = 25; - else - heartBeatAxisTick = 50; - for (heartBeatAxisMax = heartBeatAxisMin; heartBeatAxisMax < plotInfo.maxhr; heartBeatAxisMax += heartBeatAxisTick); - heartBeatAxis->setBounds(heartBeatAxisMin, heartBeatAxisMax + 1); - heartBeatAxis->setTickInterval(heartBeatAxisTick); - heartBeatAxis->updateTicks(animSpeed); // this shows the ticks + heartBeatAxis->setBounds(plotInfo.minhr, plotInfo.maxhr); + heartBeatAxis->updateTicks(animSpeed); } percentageAxis->setBounds(0, 100); @@ -430,22 +412,6 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM if (calcMax) timeAxis->setBounds(0.0, maxtime); - int i, incr; - static int increments[8] = { 10, 20, 30, 60, 5 * 60, 10 * 60, 15 * 60, 30 * 60 }; - /* Time markers: at most every 10 seconds, but no more than 12 markers. - * We start out with 10 seconds and increment up to 30 minutes, - * depending on the dive time. - * This allows for 6h dives - enough (I hope) for even the craziest - * divers - but just in case, for those 8h depth-record-breaking dives, - * we double the interval if this still doesn't get us to 12 or fewer - * time markers */ - i = 0; - while (i < 7 && maxtime / increments[i] > 12) - i++; - incr = increments[i]; - while (maxtime / incr > 12) - incr *= 2; - timeAxis->setTickInterval(incr); timeAxis->updateTicks(animSpeed); cylinderPressureAxis->setBounds(plotInfo.minpressure, plotInfo.maxpressure);