1 | // Copyright (C) 2024 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include <QtCore/QPointF> |
5 | #include <QtGraphs/QLineSeries> |
6 | #include "private/qgraphpointanimation_p.h" |
7 | #include "private/qgraphtransition_p.h" |
8 | #include "private/qxyseries_p.h" |
9 | |
10 | /*! |
11 | \qmltype GraphPointAnimation |
12 | \inqmlmodule QtGraphs |
13 | \ingroup graphs_qml_2D |
14 | \inherits XYSeriesAnimation |
15 | \brief An animation type which signifies the animation for points. |
16 | |
17 | GraphPointAnimation is an animation type derived from QVariantAnimation which defines how points are animated. |
18 | It can make use of QVariantAnimation functionality and properties for its animations, such as \c duration and \c easing. |
19 | These animations are housed inside of a QParallelAnimationGroup and hence will run in parallel. |
20 | |
21 | This example shows how to use a GraphPointAnimation to set points to animate with |
22 | a \c duration of 1000ms and \c easing of OutCubic: |
23 | |
24 | \snippet doc_src_qmlgraphs.cpp 12 |
25 | |
26 | For XYSeries, this is considered to be the main list of points defined inside |
27 | the series. The point is linearly interpolated from the start to the end value. |
28 | |
29 | \note GraphPointAnimation currently supports animating only the last point in |
30 | a series when a point is appended or removed. If a point is replaced, the |
31 | animation will be triggered regardless of the point’s index within the |
32 | series. |
33 | |
34 | \sa GraphTransition, SplineControlAnimation |
35 | */ |
36 | |
37 | QGraphPointAnimation::QGraphPointAnimation(QObject *parent) |
38 | : QXYSeriesAnimation(parent) |
39 | { |
40 | setDuration(800); |
41 | setEasingCurve(QEasingCurve::OutCubic); |
42 | } |
43 | |
44 | QGraphPointAnimation::~QGraphPointAnimation() {} |
45 | |
46 | QGraphAnimation::GraphAnimationType QGraphPointAnimation::animationType() |
47 | { |
48 | return QGraphAnimation::GraphAnimationType::GraphPoint; |
49 | } |
50 | |
51 | void QGraphPointAnimation::setAnimatingValue(const QVariant &start, const QVariant &end) |
52 | { |
53 | setStartValue(start); |
54 | setEndValue(end); |
55 | } |
56 | |
57 | QVariant QGraphPointAnimation::interpolated(const QVariant &start, |
58 | const QVariant &end, |
59 | qreal progress) const |
60 | { |
61 | auto startPoint = qvariant_cast<QPointF>(v: start); |
62 | auto endPoint = qvariant_cast<QPointF>(v: end); |
63 | |
64 | auto interpolatedPoint = QPointF{ |
65 | qreal(startPoint.x() * (1.0 - progress) + endPoint.x() * progress), |
66 | qreal(startPoint.y() * (1.0 - progress) + endPoint.y() * progress), |
67 | }; |
68 | |
69 | return QVariant::fromValue(value: interpolatedPoint); |
70 | } |
71 | |
72 | void QGraphPointAnimation::animate() |
73 | { |
74 | // Hierarchy should look like GraphAnimation -> ParallelAnimationGroup -> GraphTransition -> QXYSeries |
75 | auto series = qobject_cast<QXYSeries *>(object: parent()->parent()->parent()); |
76 | |
77 | if (!series) |
78 | return; |
79 | |
80 | if (animating() == QGraphAnimation::AnimationState::Playing) { |
81 | end(); |
82 | m_activePointIndex = m_newPointIndex; |
83 | } |
84 | |
85 | setAnimating(QGraphAnimation::AnimationState::Playing); |
86 | |
87 | auto &pointList = series->d_func()->m_points; |
88 | |
89 | switch (m_currentTransitionType) { |
90 | default: |
91 | case QGraphTransition::TransitionType::PointAdded: { |
92 | pointList.append(t: series->points().size() >= 1 ? pointList.last() : m_newPoint); |
93 | |
94 | auto startv = QVariant::fromValue(value: pointList.last()); |
95 | auto endv = QVariant::fromValue(value: m_newPoint); |
96 | |
97 | setAnimatingValue(start: startv, end: endv); |
98 | } break; |
99 | case QGraphTransition::TransitionType::PointReplaced: { |
100 | auto startv = QVariant::fromValue(value: pointList[m_activePointIndex]); |
101 | auto endv = QVariant::fromValue(value: m_newPoint); |
102 | |
103 | setAnimatingValue(start: startv, end: endv); |
104 | } break; |
105 | case QGraphTransition::TransitionType::PointRemoved: { |
106 | if (series->points().size() < 1) |
107 | break; |
108 | |
109 | auto startv = QVariant::fromValue(value: pointList[pointList.size() - 1]); |
110 | auto endv = QVariant::fromValue( |
111 | value: pointList[pointList.size() > 1 ? pointList.size() - 2 : pointList.size() - 1]); |
112 | |
113 | setAnimatingValue(start: startv, end: endv); |
114 | } break; |
115 | } |
116 | |
117 | m_previousTransitionType = m_currentTransitionType; |
118 | } |
119 | |
120 | void QGraphPointAnimation::end() |
121 | { |
122 | auto series = qobject_cast<QXYSeries *>(object: parent()->parent()->parent()); |
123 | |
124 | if (!series || animating() == QGraphAnimation::AnimationState::Stopped) { |
125 | m_previousTransitionType = m_currentTransitionType; |
126 | return; |
127 | } |
128 | |
129 | setAnimating(QGraphAnimation::AnimationState::Stopped); |
130 | stop(); |
131 | |
132 | auto &points = series->d_func()->m_points; |
133 | |
134 | switch (m_previousTransitionType) { |
135 | default: |
136 | case QGraphTransition::TransitionType::PointAdded: { |
137 | points.replace(i: m_activePointIndex, t: qvariant_cast<QPointF>(v: endValue())); |
138 | emit series->pointAdded(index: points.size() - 1); |
139 | emit series->countChanged(); |
140 | } break; |
141 | case QGraphTransition::TransitionType::PointReplaced: { |
142 | points.replace(i: m_activePointIndex, t: qvariant_cast<QPointF>(v: endValue())); |
143 | emit series->pointReplaced(index: m_activePointIndex); |
144 | } break; |
145 | case QGraphTransition::TransitionType::PointRemoved: { |
146 | points.remove(i: points.size() - 1); |
147 | emit series->countChanged(); |
148 | emit series->pointRemoved(index: points.size() - 1); |
149 | } break; |
150 | } |
151 | |
152 | m_previousTransitionType = m_currentTransitionType; |
153 | emit series->update(); |
154 | } |
155 | |
156 | void QGraphPointAnimation::valueUpdated(const QVariant &value) |
157 | { |
158 | auto series = qobject_cast<QXYSeries *>(object: parent()->parent()->parent()); |
159 | |
160 | if (!series) |
161 | return; |
162 | |
163 | auto val = qvariant_cast<QPointF>(v: value); |
164 | auto &points = series->d_func()->m_points; |
165 | |
166 | switch (m_currentTransitionType) { |
167 | default: |
168 | case QGraphTransition::TransitionType::PointAdded: { |
169 | points.replace(i: m_activePointIndex, t: val); |
170 | } break; |
171 | case QGraphTransition::TransitionType::PointReplaced: { |
172 | points.replace(i: m_activePointIndex, t: val); |
173 | } break; |
174 | case QGraphTransition::TransitionType::PointRemoved: { |
175 | if (points.size() > 1) |
176 | points.replace(i: points.size() - 1, t: val); |
177 | } break; |
178 | } |
179 | |
180 | emit series->update(); |
181 | } |
182 | |