| 1 | // Copyright (C) 2016 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
| 3 | |
| 4 | #include <private/splineanimation_p.h> |
| 5 | #include <private/splinechartitem_p.h> |
| 6 | #include <QtCore/QDebug> |
| 7 | |
| 8 | |
| 9 | QT_BEGIN_NAMESPACE |
| 10 | |
| 11 | SplineAnimation::SplineAnimation(SplineChartItem *item, int duration, QEasingCurve &curve) |
| 12 | : XYAnimation(item, duration, curve), |
| 13 | m_item(item), |
| 14 | m_valid(false) |
| 15 | { |
| 16 | } |
| 17 | |
| 18 | SplineAnimation::~SplineAnimation() |
| 19 | { |
| 20 | } |
| 21 | |
| 22 | void SplineAnimation::setup(const QList<QPointF> &oldPoints, const QList<QPointF> &newPoints, |
| 23 | const QList<QPointF> &oldControlPoints, |
| 24 | const QList<QPointF> &newControlPoints, int index) |
| 25 | { |
| 26 | if (newPoints.size() * 2 - 2 != newControlPoints.size() || newControlPoints.size() < 2) { |
| 27 | m_valid = false; |
| 28 | m_dirty = false; |
| 29 | m_item->setGeometryPoints(newPoints); |
| 30 | m_item->setControlGeometryPoints(newControlPoints); |
| 31 | m_item->setDirty(false); |
| 32 | m_item->updateGeometry(); |
| 33 | return; |
| 34 | } |
| 35 | |
| 36 | m_type = NewAnimation; |
| 37 | |
| 38 | if (state() != QAbstractAnimation::Stopped) { |
| 39 | stop(); |
| 40 | m_dirty = false; |
| 41 | } |
| 42 | |
| 43 | if (!m_dirty) { |
| 44 | m_dirty = true; |
| 45 | m_oldSpline.first = oldPoints; |
| 46 | m_oldSpline.second = oldControlPoints; |
| 47 | } |
| 48 | |
| 49 | m_newSpline.first = newPoints; |
| 50 | m_newSpline.second = newControlPoints; |
| 51 | |
| 52 | |
| 53 | int x = m_oldSpline.first.size(); |
| 54 | int y = m_newSpline.first.size(); |
| 55 | |
| 56 | if (x - y == 1 && index >= 0 && y > 0) { |
| 57 | //remove point |
| 58 | if (index > 0) { |
| 59 | m_newSpline.first.insert(i: index, t: newPoints[index - 1]); |
| 60 | m_newSpline.second.insert(i: (index - 1) * 2, t: newPoints[index - 1]); |
| 61 | m_newSpline.second.insert(i: (index - 1) * 2 + 1, t: newPoints[index - 1]); |
| 62 | } else { |
| 63 | m_newSpline.first.insert(i: 0, t: newPoints[index]); |
| 64 | m_newSpline.second.insert(i: 0, t: newPoints[index]); |
| 65 | m_newSpline.second.insert(i: 1, t: newPoints[index]); |
| 66 | } |
| 67 | m_index = index; |
| 68 | m_type = RemovePointAnimation; |
| 69 | } |
| 70 | |
| 71 | if (x - y == -1 && index >= 0) { |
| 72 | //add point |
| 73 | if (index > 0) { |
| 74 | m_oldSpline.first.insert(i: index, t: newPoints[index - 1]); |
| 75 | m_oldSpline.second.insert(i: (index - 1) * 2, t: newPoints[index - 1]); |
| 76 | m_oldSpline.second.insert(i: (index - 1) * 2 + 1, t: newPoints[index - 1]); |
| 77 | } else { |
| 78 | m_oldSpline.first.insert(i: 0, t: newPoints[index]); |
| 79 | m_oldSpline.second.insert(i: 0, t: newPoints[index]); |
| 80 | m_oldSpline.second.insert(i: 1, t: newPoints[index]); |
| 81 | } |
| 82 | m_index = index; |
| 83 | m_type = AddPointAnimation; |
| 84 | } |
| 85 | |
| 86 | x = m_oldSpline.first.size(); |
| 87 | y = m_newSpline.first.size(); |
| 88 | |
| 89 | if (x != y) { |
| 90 | m_type = NewAnimation; |
| 91 | } else if (m_type == NewAnimation) { |
| 92 | m_type = ReplacePointAnimation; |
| 93 | } |
| 94 | |
| 95 | |
| 96 | setKeyValueAt(step: 0.0, value: QVariant::fromValue(value: m_oldSpline)); |
| 97 | setKeyValueAt(step: 1.0, value: QVariant::fromValue(value: m_newSpline)); |
| 98 | |
| 99 | m_valid = true; |
| 100 | |
| 101 | } |
| 102 | |
| 103 | QVariant SplineAnimation::interpolated(const QVariant &start, const QVariant &end, qreal progress) const |
| 104 | { |
| 105 | |
| 106 | SplineVector startPair = qvariant_cast< SplineVector >(v: start); |
| 107 | SplineVector endPair = qvariant_cast< SplineVector >(v: end); |
| 108 | SplineVector result; |
| 109 | |
| 110 | switch (animationType()) { |
| 111 | case RemovePointAnimation: |
| 112 | case AddPointAnimation: |
| 113 | case ReplacePointAnimation: { |
| 114 | if (startPair.first.size() != endPair.first.size()) |
| 115 | break; |
| 116 | Q_ASSERT(startPair.first.size() * 2 - 2 == startPair.second.size()); |
| 117 | Q_ASSERT(endPair.first.size() * 2 - 2 == endPair.second.size()); |
| 118 | for (int i = 0; i < endPair.first.size(); i++) { |
| 119 | qreal x = startPair.first[i].x() + ((endPair.first[i].x() - startPair.first[i].x()) * progress); |
| 120 | qreal y = startPair.first[i].y() + ((endPair.first[i].y() - startPair.first[i].y()) * progress); |
| 121 | result.first << QPointF(x, y); |
| 122 | if (i + 1 >= endPair.first.size()) |
| 123 | continue; |
| 124 | x = startPair.second[i * 2].x() + ((endPair.second[i * 2].x() - startPair.second[i * 2].x()) * progress); |
| 125 | y = startPair.second[i * 2].y() + ((endPair.second[i * 2].y() - startPair.second[i * 2].y()) * progress); |
| 126 | result.second << QPointF(x, y); |
| 127 | x = startPair.second[i * 2 + 1].x() + ((endPair.second[i * 2 + 1].x() - startPair.second[i * 2 + 1].x()) * progress); |
| 128 | y = startPair.second[i * 2 + 1].y() + ((endPair.second[i * 2 + 1].y() - startPair.second[i * 2 + 1].y()) * progress); |
| 129 | result.second << QPointF(x, y); |
| 130 | } |
| 131 | } |
| 132 | break; |
| 133 | case NewAnimation: { |
| 134 | Q_ASSERT(endPair.first.size() * 2 - 2 == endPair.second.size()); |
| 135 | int count = endPair.first.size() * qBound(min: qreal(0), val: progress, max: qreal(1)); |
| 136 | for (int i = 0; i < count; i++) { |
| 137 | result.first << endPair.first[i]; |
| 138 | if (i + 1 == count) |
| 139 | break; |
| 140 | result.second << endPair.second[2 * i]; |
| 141 | result.second << endPair.second[2 * i + 1]; |
| 142 | } |
| 143 | } |
| 144 | break; |
| 145 | default: |
| 146 | qWarning() << "Unknown type of animation" ; |
| 147 | break; |
| 148 | } |
| 149 | |
| 150 | return QVariant::fromValue(value: result); |
| 151 | } |
| 152 | |
| 153 | void SplineAnimation::updateCurrentValue(const QVariant &value) |
| 154 | { |
| 155 | if (state() != QAbstractAnimation::Stopped && m_valid) { //workaround |
| 156 | const auto pair = qvariant_cast<QPair<QList<QPointF>, QList<QPointF>>>(v: value); |
| 157 | m_item->setGeometryPoints(pair.first); |
| 158 | m_item->setControlGeometryPoints(pair.second); |
| 159 | m_item->updateGeometry(); |
| 160 | m_item->setDirty(true); |
| 161 | m_dirty = false; |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | void SplineAnimation::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) |
| 166 | { |
| 167 | XYAnimation::updateState(newState, oldState); |
| 168 | |
| 169 | if (oldState == QAbstractAnimation::Running && newState == QAbstractAnimation::Stopped) { |
| 170 | if (m_item->isDirty() && m_type == RemovePointAnimation) { |
| 171 | if (!m_newSpline.first.isEmpty()) { |
| 172 | if (m_index) { |
| 173 | m_newSpline.first.remove(i: m_index); |
| 174 | m_newSpline.second.remove(i: (m_index - 1) * 2); |
| 175 | m_newSpline.second.remove(i: (m_index - 1) * 2); |
| 176 | } else { |
| 177 | m_newSpline.first.remove(i: 0); |
| 178 | m_newSpline.second.remove(i: 0); |
| 179 | m_newSpline.second.remove(i: 0); |
| 180 | } |
| 181 | } |
| 182 | m_item->setGeometryPoints(m_newSpline.first); |
| 183 | m_item->setControlGeometryPoints(m_newSpline.second); |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | if (oldState == QAbstractAnimation::Stopped && newState == QAbstractAnimation::Running) { |
| 188 | if (!m_valid) |
| 189 | stop(); |
| 190 | } |
| 191 | } |
| 192 | |
| 193 | QT_END_NAMESPACE |
| 194 | |