From 5c5c0c4880fcec99053398ea9f8ab8f92969bbcb Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 2 Dec 2021 23:53:17 +0100 Subject: [PATCH] profile: redo animation of labels and grid lines The old animation was weird: it would reuse the labels based on the index, not on the value. Thus, with the new scaling code, sometimes there was no animation at all, if the value, but not the position changed. Consider the values instead and let labels appear/disappear. This makes things slightly more complex. While changing this code, create our own animation-class. Thus, we can avoid having the dive axes being QObjects. Signed-off-by: Berthold Stoeger --- profile-widget/divecartesianaxis.cpp | 233 ++++++++++++++++----------- profile-widget/divecartesianaxis.h | 38 +++-- profile-widget/profilescene.cpp | 46 ++++++ profile-widget/profilescene.h | 5 + 4 files changed, 216 insertions(+), 106 deletions(-) diff --git a/profile-widget/divecartesianaxis.cpp b/profile-widget/divecartesianaxis.cpp index 1515b78d6..12b9999c4 100644 --- a/profile-widget/divecartesianaxis.cpp +++ b/profile-widget/divecartesianaxis.cpp @@ -70,15 +70,6 @@ void DiveCartesianAxis::setTransform(double a, double b) transform.b = b; } -template -void emptyList(QList &list, int steps, int speed) -{ - while (list.size() > steps) { - T *removedItem = list.takeLast(); - Animations::animDelete(removedItem, speed); - } -} - double DiveCartesianAxis::width() const { return labelWidth + labelSpaceHorizontal * dpr; @@ -151,6 +142,10 @@ void DiveCartesianAxis::updateTicks(int animSpeed) if (!textVisibility && !lineVisibility) return; // Nothing to display... + // Remember the old range for animations. + double dataMaxOld = dataMax; + double dataMinOld = dataMin; + // Guess the number of tick marks. QLineF m = line(); double spaceNeeded = position == Position::Bottom ? labelWidth * 3.0 / 2.0 @@ -188,8 +183,6 @@ void DiveCartesianAxis::updateTicks(int animSpeed) numTicks = lrint((lastDisplay - firstDisplay) / intervalDisplay) + 1; numTicks = std::max(numTicks, 0); - emptyList(labels, numTicks, animSpeed); - emptyList(lines, numTicks, animSpeed); if (numTicks == 0) return; @@ -209,100 +202,151 @@ void DiveCartesianAxis::updateTicks(int animSpeed) (inverted ? m.x2() - offsetScreen : m.x1() + offsetScreen) : (inverted ? m.y1() + offsetScreen : m.y2() - offsetScreen); - if (textVisibility) - updateLabels(numTicks, firstPosScreen, firstValue, stepScreen, stepValue, animSpeed); - if (lineVisibility) - updateLines(numTicks, firstPosScreen, stepScreen, animSpeed); + updateLabels(numTicks, firstPosScreen, firstValue, stepScreen, stepValue, animSpeed, dataMinOld, dataMaxOld); } -void DiveCartesianAxis::updateLabels(int numTicks, double firstPosScreen, double firstValue, double stepScreen, double stepValue, int animSpeed) -{ - for (int i = 0, count = labels.size(); i < count; i++, firstValue += stepValue) { - double childPos = ((position == Position::Bottom) != inverted) ? - firstPosScreen + i * stepScreen : - firstPosScreen - i * stepScreen; - labels[i]->set(textForValue(firstValue), textColor); - switch (position) { - default: - case Position::Bottom: - Animations::moveTo(labels[i], animSpeed, childPos, rect.bottom() + labelSpaceVertical * dpr); - break; - case Position::Left: - Animations::moveTo(labels[i], animSpeed, rect.left() - labelSpaceHorizontal * dpr, childPos); - break; - case Position::Right: - Animations::moveTo(labels[i], animSpeed, rect.right() + labelSpaceHorizontal * dpr, childPos); - break; - } - } - // Add the rest of the needed labels. - for (int i = labels.size(); i < numTicks; i++, firstValue += stepValue) { - double childPos = ((position == Position::Bottom) != inverted) ? - firstPosScreen + i * stepScreen : - firstPosScreen - i * stepScreen; +QPointF DiveCartesianAxis::labelPos(double pos) const +{ + return position == Position::Bottom ? QPointF(pos, rect.bottom() + labelSpaceVertical * dpr) : + position == Position::Left ? QPointF(rect.left() - labelSpaceHorizontal * dpr, pos) : + QPointF(rect.right() + labelSpaceHorizontal * dpr, pos); +} + +QLineF DiveCartesianAxis::linePos(double pos) const +{ + return position == Position::Bottom ? QLineF(pos, rect.top(), pos, rect.bottom()) : + QLineF(rect.left(), pos, rect.right(), pos); +} + +void DiveCartesianAxis::updateLabel(Label &label, double opacityEnd, double pos) const +{ + label.opacityStart = textVisibility ? label.label->opacity() + : label.line->opacity(); + label.opacityEnd = opacityEnd; + if (textVisibility) { + label.labelPosStart = label.label->pos(); + label.labelPosEnd = labelPos(pos); + } + if (lineVisibility) { + label.lineStart = label.line->line(); + label.lineEnd = linePos(pos); + } +} + +DiveCartesianAxis::Label DiveCartesianAxis::createLabel(double value, double pos, double dataMinOld, double dataMaxOld, int animSpeed) +{ + Label label { value, 0.0, 1.0 }; + double posStart = posAtValue(value, dataMaxOld, dataMinOld); + if (textVisibility) { + label.labelPosStart = labelPos(posStart); + label.labelPosEnd = labelPos(pos); int alignFlags = position == Position::Bottom ? Qt::AlignTop | Qt::AlignHCenter : position == Position::Left ? Qt::AlignVCenter | Qt::AlignLeft: Qt::AlignVCenter | Qt::AlignRight; - DiveTextItem *label = new DiveTextItem(dpr, labelScale, alignFlags, this); - label->set(textForValue(firstValue), textColor); - label->setZValue(1); - labels.push_back(label); - switch (position) { - default: - case Position::Bottom: - label->setPos(scene.sceneRect().width() + 10, rect.bottom() + labelSpaceVertical * dpr); // position it outside of the scene; - Animations::moveTo(labels[i], animSpeed, childPos, rect.bottom() + labelSpaceVertical * dpr); - break; - case Position::Left: - label->setPos(rect.left() - labelSpaceHorizontal * dpr, scene.sceneRect().height() + 10); - Animations::moveTo(labels[i], animSpeed, rect.left() - labelSpaceHorizontal * dpr, childPos); - break; - case Position::Right: - label->setPos(rect.right() + labelSpaceHorizontal * dpr, scene.sceneRect().height() + 10); - Animations::moveTo(labels[i], animSpeed, rect.right() + labelSpaceHorizontal * dpr, childPos); - break; - } + label.label = std::make_unique(dpr, labelScale, alignFlags, this); + label.label->set(textForValue(value), textColor); + label.label->setZValue(1); + label.label->setPos(animSpeed <= 0 ? label.labelPosEnd : label.labelPosStart); + label.label->setOpacity(animSpeed <= 0 ? 1.0 : 0.0); } + if (lineVisibility) { + label.lineStart = linePos(posStart); + label.lineEnd = linePos(pos); + label.line = std::make_unique(this); + label.line->setPen(gridPen); + label.line->setZValue(0); + label.line->setLine(animSpeed <= 0 ? label.lineEnd : label.lineStart); + label.line->setOpacity(animSpeed <= 0 ? 1.0 : 0.0); + } + return label; } -void DiveCartesianAxis::updateLines(int numTicks, double firstPosScreen, double stepScreen, int animSpeed) +void DiveCartesianAxis::updateLabels(int numTicks, double firstPosScreen, double firstValue, double stepScreen, double stepValue, + int animSpeed, double dataMinOld, double dataMaxOld) { - for (int i = 0, count = lines.size(); i < count; i++) { - double childPos = ((position == Position::Bottom) != inverted) ? - firstPosScreen + i * stepScreen : - firstPosScreen - i * stepScreen; + if (animSpeed <= 0) + labels.clear(); // No animation? Simply redo the labels. - if (position == Position::Bottom) { - // Fix size in case the scene changed - QLineF old = lines[i]->line(); - lines[i]->setLine(old.x1(), old.y1(), old.x1(), old.y1() + rect.height()); - Animations::moveTo(lines[i], animSpeed, childPos, rect.top()); + std::vector