1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4/*!
5 \class QGraphicsItemAnimation
6 \brief The QGraphicsItemAnimation class provides simple animation
7 support for QGraphicsItem.
8 \since 4.2
9 \ingroup graphicsview-api
10 \inmodule QtWidgets
11 \deprecated
12
13 The QGraphicsItemAnimation class animates a QGraphicsItem. You can
14 schedule changes to the item's transformation matrix at
15 specified steps. The QGraphicsItemAnimation class has a
16 current step value. When this value changes the transformations
17 scheduled at that step are performed. The current step of the
18 animation is set with the \c setStep() function.
19
20 QGraphicsItemAnimation will do a simple linear interpolation
21 between the nearest adjacent scheduled changes to calculate the
22 matrix. For instance, if you set the position of an item at values
23 0.0 and 1.0, the animation will show the item moving in a straight
24 line between these positions. The same is true for scaling and
25 rotation.
26
27 It is usual to use the class with a QTimeLine. The timeline's
28 \l{QTimeLine::}{valueChanged()} signal is then connected to the
29 \c setStep() slot. For example, you can set up an item for rotation
30 by calling \c setRotationAt() for different step values.
31 The animations timeline is set with the setTimeLine() function.
32
33 An example animation with a timeline follows:
34
35 \snippet timeline/main.cpp 0
36
37 Note that steps lie between 0.0 and 1.0. It may be necessary to use
38 \l{QTimeLine::}{setUpdateInterval()}. The default update interval
39 is 40 ms. A scheduled transformation cannot be removed when set,
40 so scheduling several transformations of the same kind (e.g.,
41 rotations) at the same step is not recommended.
42
43 \sa QTimeLine, {Graphics View Framework}
44*/
45
46#include "qgraphicsitemanimation.h"
47
48#include "qgraphicsitem.h"
49
50#include <QtCore/qtimeline.h>
51#include <QtCore/qpoint.h>
52#include <QtCore/qpointer.h>
53#include <QtCore/qpair.h>
54
55#include <algorithm>
56
57QT_BEGIN_NAMESPACE
58
59static inline bool check_step_valid(qreal step, const char *method)
60{
61 if (!(step >= 0 && step <= 1)) {
62 qWarning(msg: "QGraphicsItemAnimation::%s: invalid step = %f", method, step);
63 return false;
64 }
65 return true;
66}
67
68class QGraphicsItemAnimationPrivate
69{
70public:
71 inline QGraphicsItemAnimationPrivate()
72 : q(nullptr), timeLine(nullptr), item(nullptr), step(0)
73 { }
74
75 QGraphicsItemAnimation *q;
76
77 QPointer<QTimeLine> timeLine;
78 QGraphicsItem *item;
79
80 QPointF startPos;
81 QTransform startTransform;
82
83 qreal step;
84
85 struct Pair {
86 bool operator <(const Pair &other) const
87 { return step < other.step; }
88 bool operator==(const Pair &other) const
89 { return step == other.step; }
90 qreal step;
91 qreal value;
92 };
93 QList<Pair> xPosition;
94 QList<Pair> yPosition;
95 QList<Pair> rotation;
96 QList<Pair> verticalScale;
97 QList<Pair> horizontalScale;
98 QList<Pair> verticalShear;
99 QList<Pair> horizontalShear;
100 QList<Pair> xTranslation;
101 QList<Pair> yTranslation;
102
103 qreal linearValueForStep(qreal step, const QList<Pair> &source, qreal defaultValue = 0);
104 void insertUniquePair(qreal step, qreal value, QList<Pair> *binList, const char *method);
105};
106Q_DECLARE_TYPEINFO(QGraphicsItemAnimationPrivate::Pair, Q_PRIMITIVE_TYPE);
107
108qreal QGraphicsItemAnimationPrivate::linearValueForStep(qreal step, const QList<Pair> &source,
109 qreal defaultValue)
110{
111 if (source.isEmpty())
112 return defaultValue;
113 step = qMin<qreal>(a: qMax<qreal>(a: step, b: 0), b: 1);
114
115 if (step == 1)
116 return source.back().value;
117
118 qreal stepBefore = 0;
119 qreal stepAfter = 1;
120 qreal valueBefore = source.front().step == 0 ? source.front().value : defaultValue;
121 qreal valueAfter = source.back().value;
122
123 // Find the closest step and value before the given step.
124 for (int i = 0; i < source.size() && step >= source[i].step; ++i) {
125 stepBefore = source[i].step;
126 valueBefore = source[i].value;
127 }
128
129 // Find the closest step and value after the given step.
130 for (int i = source.size() - 1; i >= 0 && step < source[i].step; --i) {
131 stepAfter = source[i].step;
132 valueAfter = source[i].value;
133 }
134
135 // Do a simple linear interpolation.
136 return valueBefore + (valueAfter - valueBefore) * ((step - stepBefore) / (stepAfter - stepBefore));
137}
138
139void QGraphicsItemAnimationPrivate::insertUniquePair(qreal step, qreal value, QList<Pair> *binList,
140 const char *method)
141{
142 if (!check_step_valid(step, method))
143 return;
144
145 const Pair pair = { .step: step, .value: value };
146
147 const QList<Pair>::iterator result = std::lower_bound(first: binList->begin(), last: binList->end(), val: pair);
148 if (result == binList->end() || pair < *result)
149 binList->insert(before: result, t: pair);
150 else
151 result->value = value;
152}
153
154/*!
155 Constructs an animation object with the given \a parent.
156*/
157QGraphicsItemAnimation::QGraphicsItemAnimation(QObject *parent)
158 : QObject(parent), d(new QGraphicsItemAnimationPrivate)
159{
160 d->q = this;
161}
162
163/*!
164 Destroys the animation object.
165*/
166QGraphicsItemAnimation::~QGraphicsItemAnimation()
167{
168 delete d;
169}
170
171/*!
172 Returns the item on which the animation object operates.
173
174 \sa setItem()
175*/
176QGraphicsItem *QGraphicsItemAnimation::item() const
177{
178 return d->item;
179}
180
181/*!
182 Sets the specified \a item to be used in the animation.
183
184 \sa item()
185*/
186void QGraphicsItemAnimation::setItem(QGraphicsItem *item)
187{
188 d->item = item;
189 d->startPos = d->item->pos();
190}
191
192/*!
193 Returns the timeline object used to control the rate at which the animation
194 occurs.
195
196 \sa setTimeLine()
197*/
198QTimeLine *QGraphicsItemAnimation::timeLine() const
199{
200 return d->timeLine;
201}
202
203/*!
204 Sets the timeline object used to control the rate of animation to the \a timeLine
205 specified.
206
207 \sa timeLine()
208*/
209void QGraphicsItemAnimation::setTimeLine(QTimeLine *timeLine)
210{
211 if (d->timeLine == timeLine)
212 return;
213 if (d->timeLine)
214 delete d->timeLine;
215 if (!timeLine)
216 return;
217 d->timeLine = timeLine;
218 connect(sender: timeLine, SIGNAL(valueChanged(qreal)), receiver: this, SLOT(setStep(qreal)));
219}
220
221/*!
222 Returns the position of the item at the given \a step value.
223
224 \sa setPosAt()
225*/
226QPointF QGraphicsItemAnimation::posAt(qreal step) const
227{
228 check_step_valid(step, method: "posAt");
229 return QPointF(d->linearValueForStep(step, source: d->xPosition, defaultValue: d->startPos.x()),
230 d->linearValueForStep(step, source: d->yPosition, defaultValue: d->startPos.y()));
231}
232
233/*!
234 \fn void QGraphicsItemAnimation::setPosAt(qreal step, const QPointF &point)
235
236 Sets the position of the item at the given \a step value to the \a point specified.
237
238 \sa posAt()
239*/
240void QGraphicsItemAnimation::setPosAt(qreal step, const QPointF &pos)
241{
242 d->insertUniquePair(step, value: pos.x(), binList: &d->xPosition, method: "setPosAt");
243 d->insertUniquePair(step, value: pos.y(), binList: &d->yPosition, method: "setPosAt");
244}
245
246/*!
247 Returns all explicitly inserted positions.
248
249 \sa posAt(), setPosAt()
250*/
251QList<QPair<qreal, QPointF> > QGraphicsItemAnimation::posList() const
252{
253 QList<QPair<qreal, QPointF> > list;
254 const int xPosCount = d->xPosition.size();
255 list.reserve(asize: xPosCount);
256 for (int i = 0; i < xPosCount; ++i)
257 list << QPair<qreal, QPointF>(d->xPosition.at(i).step, QPointF(d->xPosition.at(i).value, d->yPosition.at(i).value));
258
259 return list;
260}
261
262/*!
263 Returns the transform used for the item at the specified \a step value.
264
265 \since 5.14
266*/
267QTransform QGraphicsItemAnimation::transformAt(qreal step) const
268{
269 check_step_valid(step, method: "transformAt");
270
271 QTransform transform;
272 if (!d->rotation.isEmpty())
273 transform.rotate(a: rotationAt(step));
274 if (!d->verticalScale.isEmpty())
275 transform.scale(sx: horizontalScaleAt(step), sy: verticalScaleAt(step));
276 if (!d->verticalShear.isEmpty())
277 transform.shear(sh: horizontalShearAt(step), sv: verticalShearAt(step));
278 if (!d->xTranslation.isEmpty())
279 transform.translate(dx: xTranslationAt(step), dy: yTranslationAt(step));
280 return transform;
281}
282
283/*!
284 Returns the angle at which the item is rotated at the specified \a step value.
285
286 \sa setRotationAt()
287*/
288qreal QGraphicsItemAnimation::rotationAt(qreal step) const
289{
290 check_step_valid(step, method: "rotationAt");
291 return d->linearValueForStep(step, source: d->rotation);
292}
293
294/*!
295 Sets the rotation of the item at the given \a step value to the \a angle specified.
296
297 \sa rotationAt()
298*/
299void QGraphicsItemAnimation::setRotationAt(qreal step, qreal angle)
300{
301 d->insertUniquePair(step, value: angle, binList: &d->rotation, method: "setRotationAt");
302}
303
304/*!
305 Returns all explicitly inserted rotations.
306
307 \sa rotationAt(), setRotationAt()
308*/
309QList<QPair<qreal, qreal> > QGraphicsItemAnimation::rotationList() const
310{
311 QList<QPair<qreal, qreal> > list;
312 const int numRotations = d->rotation.size();
313 list.reserve(asize: numRotations);
314 for (int i = 0; i < numRotations; ++i)
315 list << QPair<qreal, qreal>(d->rotation.at(i).step, d->rotation.at(i).value);
316
317 return list;
318}
319
320/*!
321 Returns the horizontal translation of the item at the specified \a step value.
322
323 \sa setTranslationAt()
324*/
325qreal QGraphicsItemAnimation::xTranslationAt(qreal step) const
326{
327 check_step_valid(step, method: "xTranslationAt");
328 return d->linearValueForStep(step, source: d->xTranslation);
329}
330
331/*!
332 Returns the vertical translation of the item at the specified \a step value.
333
334 \sa setTranslationAt()
335*/
336qreal QGraphicsItemAnimation::yTranslationAt(qreal step) const
337{
338 check_step_valid(step, method: "yTranslationAt");
339 return d->linearValueForStep(step, source: d->yTranslation);
340}
341
342/*!
343 Sets the translation of the item at the given \a step value using the horizontal
344 and vertical coordinates specified by \a dx and \a dy.
345
346 \sa xTranslationAt(), yTranslationAt()
347*/
348void QGraphicsItemAnimation::setTranslationAt(qreal step, qreal dx, qreal dy)
349{
350 d->insertUniquePair(step, value: dx, binList: &d->xTranslation, method: "setTranslationAt");
351 d->insertUniquePair(step, value: dy, binList: &d->yTranslation, method: "setTranslationAt");
352}
353
354/*!
355 Returns all explicitly inserted translations.
356
357 \sa xTranslationAt(), yTranslationAt(), setTranslationAt()
358*/
359QList<QPair<qreal, QPointF> > QGraphicsItemAnimation::translationList() const
360{
361 QList<QPair<qreal, QPointF> > list;
362 const int numTranslations = d->xTranslation.size();
363 list.reserve(asize: numTranslations);
364 for (int i = 0; i < numTranslations; ++i)
365 list << QPair<qreal, QPointF>(d->xTranslation.at(i).step, QPointF(d->xTranslation.at(i).value, d->yTranslation.at(i).value));
366
367 return list;
368}
369
370/*!
371 Returns the vertical scale for the item at the specified \a step value.
372
373 \sa setScaleAt()
374*/
375qreal QGraphicsItemAnimation::verticalScaleAt(qreal step) const
376{
377 check_step_valid(step, method: "verticalScaleAt");
378
379 return d->linearValueForStep(step, source: d->verticalScale, defaultValue: 1);
380}
381
382/*!
383 Returns the horizontal scale for the item at the specified \a step value.
384
385 \sa setScaleAt()
386*/
387qreal QGraphicsItemAnimation::horizontalScaleAt(qreal step) const
388{
389 check_step_valid(step, method: "horizontalScaleAt");
390 return d->linearValueForStep(step, source: d->horizontalScale, defaultValue: 1);
391}
392
393/*!
394 Sets the scale of the item at the given \a step value using the horizontal and
395 vertical scale factors specified by \a sx and \a sy.
396
397 \sa verticalScaleAt(), horizontalScaleAt()
398*/
399void QGraphicsItemAnimation::setScaleAt(qreal step, qreal sx, qreal sy)
400{
401 d->insertUniquePair(step, value: sx, binList: &d->horizontalScale, method: "setScaleAt");
402 d->insertUniquePair(step, value: sy, binList: &d->verticalScale, method: "setScaleAt");
403}
404
405/*!
406 Returns all explicitly inserted scales.
407
408 \sa verticalScaleAt(), horizontalScaleAt(), setScaleAt()
409*/
410QList<QPair<qreal, QPointF> > QGraphicsItemAnimation::scaleList() const
411{
412 QList<QPair<qreal, QPointF> > list;
413 const int numScales = d->horizontalScale.size();
414 list.reserve(asize: numScales);
415 for (int i = 0; i < numScales; ++i)
416 list << QPair<qreal, QPointF>(d->horizontalScale.at(i).step, QPointF(d->horizontalScale.at(i).value, d->verticalScale.at(i).value));
417
418 return list;
419}
420
421/*!
422 Returns the vertical shear for the item at the specified \a step value.
423
424 \sa setShearAt()
425*/
426qreal QGraphicsItemAnimation::verticalShearAt(qreal step) const
427{
428 check_step_valid(step, method: "verticalShearAt");
429 return d->linearValueForStep(step, source: d->verticalShear, defaultValue: 0);
430}
431
432/*!
433 Returns the horizontal shear for the item at the specified \a step value.
434
435 \sa setShearAt()
436*/
437qreal QGraphicsItemAnimation::horizontalShearAt(qreal step) const
438{
439 check_step_valid(step, method: "horizontalShearAt");
440 return d->linearValueForStep(step, source: d->horizontalShear, defaultValue: 0);
441}
442
443/*!
444 Sets the shear of the item at the given \a step value using the horizontal and
445 vertical shear factors specified by \a sh and \a sv.
446
447 \sa verticalShearAt(), horizontalShearAt()
448*/
449void QGraphicsItemAnimation::setShearAt(qreal step, qreal sh, qreal sv)
450{
451 d->insertUniquePair(step, value: sh, binList: &d->horizontalShear, method: "setShearAt");
452 d->insertUniquePair(step, value: sv, binList: &d->verticalShear, method: "setShearAt");
453}
454
455/*!
456 Returns all explicitly inserted shears.
457
458 \sa verticalShearAt(), horizontalShearAt(), setShearAt()
459*/
460QList<QPair<qreal, QPointF> > QGraphicsItemAnimation::shearList() const
461{
462 QList<QPair<qreal, QPointF> > list;
463 const int numShears = d->horizontalShear.size();
464 list.reserve(asize: numShears);
465 for (int i = 0; i < numShears; ++i)
466 list << QPair<qreal, QPointF>(d->horizontalShear.at(i).step, QPointF(d->horizontalShear.at(i).value, d->verticalShear.at(i).value));
467
468 return list;
469}
470
471/*!
472 Clears the scheduled transformations used for the animation, but
473 retains the item and timeline.
474*/
475void QGraphicsItemAnimation::clear()
476{
477 d->xPosition.clear();
478 d->yPosition.clear();
479 d->rotation.clear();
480 d->verticalScale.clear();
481 d->horizontalScale.clear();
482 d->verticalShear.clear();
483 d->horizontalShear.clear();
484 d->xTranslation.clear();
485 d->yTranslation.clear();
486}
487
488/*!
489 \fn void QGraphicsItemAnimation::setStep(qreal step)
490
491 Sets the current \a step value for the animation, causing the
492 transformations scheduled at this step to be performed.
493*/
494void QGraphicsItemAnimation::setStep(qreal step)
495{
496 if (!check_step_valid(step, method: "setStep"))
497 return;
498
499 beforeAnimationStep(step);
500
501 d->step = step;
502 if (d->item) {
503 if (!d->xPosition.isEmpty() || !d->yPosition.isEmpty())
504 d->item->setPos(posAt(step));
505 if (!d->rotation.isEmpty()
506 || !d->verticalScale.isEmpty()
507 || !d->horizontalScale.isEmpty()
508 || !d->verticalShear.isEmpty()
509 || !d->horizontalShear.isEmpty()
510 || !d->xTranslation.isEmpty()
511 || !d->yTranslation.isEmpty()) {
512 d->item->setTransform(matrix: d->startTransform * transformAt(step));
513 }
514 }
515
516 afterAnimationStep(step);
517}
518
519/*!
520 \fn void QGraphicsItemAnimation::beforeAnimationStep(qreal step)
521
522 This method is meant to be overridden by subclassed that needs to
523 execute additional code before a new step takes place. The
524 animation \a step is provided for use in cases where the action
525 depends on its value.
526*/
527void QGraphicsItemAnimation::beforeAnimationStep(qreal step)
528{
529 Q_UNUSED(step);
530}
531
532/*!
533 \fn void QGraphicsItemAnimation::afterAnimationStep(qreal step)
534
535 This method is meant to be overridden in subclasses that need to
536 execute additional code after a new step has taken place. The
537 animation \a step is provided for use in cases where the action
538 depends on its value.
539*/
540void QGraphicsItemAnimation::afterAnimationStep(qreal step)
541{
542 Q_UNUSED(step);
543}
544
545QT_END_NAMESPACE
546
547#include "moc_qgraphicsitemanimation.cpp"
548

source code of qtbase/src/widgets/graphicsview/qgraphicsitemanimation.cpp