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

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