bd12b8817d5c3e91c3814388afb56b707081bcf2
[profile/ivi/qtdeclarative.git] / src / quick / util / qdeclarativesmoothedanimation.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdeclarativesmoothedanimation_p.h"
43 #include "qdeclarativesmoothedanimation_p_p.h"
44
45 #include "qdeclarativeanimation_p_p.h"
46
47 #include <qdeclarativeproperty.h>
48 #include <private/qdeclarativeproperty_p.h>
49
50 #include <private/qdeclarativeglobal_p.h>
51
52 #include <QtCore/qdebug.h>
53
54 #include <math.h>
55
56 #define DELAY_STOP_TIMER_INTERVAL 32
57
58 QT_BEGIN_NAMESPACE
59
60 QSmoothedAnimation::QSmoothedAnimation(QObject *parent)
61     : QAbstractAnimation(parent), to(0), velocity(200), userDuration(-1), maximumEasingTime(-1),
62       reversingMode(QDeclarativeSmoothedAnimation::Eased), initialVelocity(0),
63       trackVelocity(0), initialValue(0), invert(false), finalDuration(-1), lastTime(0)
64 {
65 }
66
67 void QSmoothedAnimation::restart()
68 {
69     initialVelocity = trackVelocity;
70     if (state() != QAbstractAnimation::Running)
71         start();
72     else
73         init();
74 }
75
76 void QSmoothedAnimation::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State /*oldState*/)
77 {
78     if (newState == QAbstractAnimation::Running)
79         init();
80 }
81
82 void QSmoothedAnimation::timerEvent(QTimerEvent *event)
83 {
84     if (event->timerId() == delayedStopTimer.timerId()) {
85         delayedStopTimer.stop();
86         stop();
87     } else {
88         QAbstractAnimation::timerEvent(event);
89     }
90 }
91
92 void QSmoothedAnimation::delayedStop()
93 {
94     if (!delayedStopTimer.isActive())
95         delayedStopTimer.start(DELAY_STOP_TIMER_INTERVAL, this);
96 }
97
98 int QSmoothedAnimation::duration() const
99 {
100     return -1;
101 }
102
103 bool QSmoothedAnimation::recalc()
104 {
105     s = to - initialValue;
106     vi = initialVelocity;
107
108     s = (invert? -1.0: 1.0) * s;
109
110     if (userDuration > 0 && velocity > 0) {
111         tf = s / velocity;
112         if (tf > (userDuration / 1000.)) tf = (userDuration / 1000.);
113     } else if (userDuration > 0) {
114         tf = userDuration / 1000.;
115     } else if (velocity > 0) {
116         tf = s / velocity;
117     } else {
118         return false;
119     }
120
121     finalDuration = ceil(tf * 1000.0);
122
123     if (maximumEasingTime == 0) {
124         a = 0;
125         d = 0;
126         tp = 0;
127         td = tf;
128         vp = velocity;
129         sp = 0;
130         sd = s;
131     } else if (maximumEasingTime != -1 && tf > (maximumEasingTime / 1000.)) {
132         qreal met = maximumEasingTime / 1000.;
133         /*       tp|       |td
134          * vp_      _______
135          *         /       \
136          * vi_    /         \
137          *                   \
138          *                    \   _ 0
139          *       |ta|      |ta|
140          */
141         qreal ta = met / 2.;
142         a = (s - (vi * tf - 0.5 * vi * ta)) / (tf * ta - ta * ta);
143
144         vp = vi + a * ta;
145         d = vp / ta;
146         tp = ta;
147         sp = vi * ta + 0.5 * a * tp * tp;
148         sd = sp + vp * (tf - 2 * ta);
149         td = tf - ta;
150     } else {
151         qreal c1 = 0.25 * tf * tf;
152         qreal c2 = 0.5 * vi * tf - s;
153         qreal c3 = -0.25 * vi * vi;
154
155         qreal a1 = (-c2 + sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1);
156
157         qreal tp1 = 0.5 * tf - 0.5 * vi / a1;
158         qreal vp1 = a1 * tp1 + vi;
159
160         qreal sp1 = 0.5 * a1 * tp1 * tp1 + vi * tp1;
161
162         a = a1;
163         d = a1;
164         tp = tp1;
165         td = tp1;
166         vp = vp1;
167         sp = sp1;
168         sd = sp1;
169     }
170     return true;
171 }
172
173 qreal QSmoothedAnimation::easeFollow(qreal time_seconds)
174 {
175     qreal value;
176     if (time_seconds < tp) {
177         trackVelocity = vi + time_seconds * a;
178         value = 0.5 * a * time_seconds * time_seconds + vi * time_seconds;
179     } else if (time_seconds < td) {
180         time_seconds -= tp;
181         trackVelocity = vp;
182         value = sp + time_seconds * vp;
183     } else if (time_seconds < tf) {
184         time_seconds -= td;
185         trackVelocity = vp - time_seconds * a;
186         value = sd - 0.5 * d * time_seconds * time_seconds + vp * time_seconds;
187     } else {
188         trackVelocity = 0;
189         value = s;
190         delayedStop();
191     }
192
193     // to normalize 's' between [0..1], divide 'value' by 's'
194     return value;
195 }
196
197 void QSmoothedAnimation::updateCurrentTime(int t)
198 {
199     qreal time_seconds = qreal(t - lastTime) / 1000.;
200
201     qreal value = easeFollow(time_seconds);
202     value *= (invert? -1.0: 1.0);
203     QDeclarativePropertyPrivate::write(target, initialValue + value,
204                                        QDeclarativePropertyPrivate::BypassInterceptor
205                                        | QDeclarativePropertyPrivate::DontRemoveBinding);
206 }
207
208 void QSmoothedAnimation::init()
209 {
210     if (velocity == 0) {
211         stop();
212         return;
213     }
214
215     if (delayedStopTimer.isActive())
216         delayedStopTimer.stop();
217
218     initialValue = target.read().toReal();
219     lastTime = this->currentTime();
220
221     if (to == initialValue) {
222         stop();
223         return;
224     }
225
226     bool hasReversed = trackVelocity != 0. &&
227                       ((!invert) == ((initialValue - to) > 0));
228
229     if (hasReversed) {
230         switch (reversingMode) {
231             default:
232             case QDeclarativeSmoothedAnimation::Eased:
233                 initialVelocity = -trackVelocity;
234                 break;
235             case QDeclarativeSmoothedAnimation::Sync:
236                 QDeclarativePropertyPrivate::write(target, to,
237                                                    QDeclarativePropertyPrivate::BypassInterceptor
238                                                    | QDeclarativePropertyPrivate::DontRemoveBinding);
239                 trackVelocity = 0;
240                 stop();
241                 return;
242             case QDeclarativeSmoothedAnimation::Immediate:
243                 initialVelocity = 0;
244                 break;
245         }
246     }
247
248     trackVelocity = initialVelocity;
249
250     invert = (to < initialValue);
251
252     if (!recalc()) {
253         QDeclarativePropertyPrivate::write(target, to,
254                                            QDeclarativePropertyPrivate::BypassInterceptor
255                                            | QDeclarativePropertyPrivate::DontRemoveBinding);
256         stop();
257         return;
258     }
259 }
260
261 /*!
262     \qmlclass SmoothedAnimation QDeclarativeSmoothedAnimation
263     \inqmlmodule QtQuick 2
264     \ingroup qml-animation-transition
265     \inherits NumberAnimation
266     \brief The SmoothedAnimation element allows a property to smoothly track a value.
267
268     A SmoothedAnimation animates a property's value to a set target value
269     using an ease in/out quad easing curve.  When the target value changes,
270     the easing curves used to animate between the old and new target values
271     are smoothly spliced together to create a smooth movement to the new
272     target value that maintains the current velocity.
273
274     The follow example shows one \l Rectangle tracking the position of another
275     using SmoothedAnimation. The green rectangle's \c x and \c y values are
276     bound to those of the red rectangle. Whenever these values change, the
277     green rectangle smoothly animates to its new position:
278
279     \snippet doc/src/snippets/declarative/smoothedanimation.qml 0
280
281     A SmoothedAnimation can be configured by setting the \l velocity at which the
282     animation should occur, or the \l duration that the animation should take.
283     If both the \l velocity and \l duration are specified, the one that results in
284     the quickest animation is chosen for each change in the target value.
285
286     For example, animating from 0 to 800 will take 4 seconds if a velocity
287     of 200 is set, will take 8 seconds with a duration of 8000 set, and will
288     take 4 seconds with both a velocity of 200 and a duration of 8000 set.
289     Animating from 0 to 20000 will take 10 seconds if a velocity of 200 is set,
290     will take 8 seconds with a duration of 8000 set, and will take 8 seconds
291     with both a velocity of 200 and a duration of 8000 set.
292
293     The default velocity of SmoothedAnimation is 200 units/second.  Note that if the range of the
294     value being animated is small, then the velocity will need to be adjusted
295     appropriately.  For example, the opacity of an item ranges from 0 - 1.0.
296     To enable a smooth animation in this range the velocity will need to be
297     set to a value such as 0.5 units/second.  Animating from 0 to 1.0 with a velocity
298     of 0.5 will take 2000 ms to complete.
299
300     Like any other animation element, a SmoothedAnimation can be applied in a
301     number of ways, including transitions, behaviors and property value
302     sources. The \l {QML Animation and Transitions} documentation shows a
303     variety of methods for creating animations.
304
305     \sa SpringAnimation, NumberAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example}
306 */
307
308 QDeclarativeSmoothedAnimation::QDeclarativeSmoothedAnimation(QObject *parent)
309 : QDeclarativeNumberAnimation(*(new QDeclarativeSmoothedAnimationPrivate), parent)
310 {
311 }
312
313 QDeclarativeSmoothedAnimation::~QDeclarativeSmoothedAnimation()
314 {
315 }
316
317 QDeclarativeSmoothedAnimationPrivate::QDeclarativeSmoothedAnimationPrivate()
318     : wrapperGroup(new QParallelAnimationGroup), anim(new QSmoothedAnimation)
319 {
320     Q_Q(QDeclarativeSmoothedAnimation);
321     QDeclarative_setParent_noEvent(wrapperGroup, q);
322     QDeclarative_setParent_noEvent(anim, q);
323 }
324
325 void QDeclarativeSmoothedAnimationPrivate::updateRunningAnimations()
326 {
327     foreach(QSmoothedAnimation* ease, activeAnimations.values()){
328         ease->maximumEasingTime = anim->maximumEasingTime;
329         ease->reversingMode = anim->reversingMode;
330         ease->velocity = anim->velocity;
331         ease->userDuration = anim->userDuration;
332         ease->init();
333     }
334 }
335
336 QAbstractAnimation* QDeclarativeSmoothedAnimation::qtAnimation()
337 {
338     Q_D(QDeclarativeSmoothedAnimation);
339     return d->wrapperGroup;
340 }
341
342 void QDeclarativeSmoothedAnimation::transition(QDeclarativeStateActions &actions,
343                                                QDeclarativeProperties &modified,
344                                                TransitionDirection direction)
345 {
346     Q_D(QDeclarativeSmoothedAnimation);
347     QDeclarativeNumberAnimation::transition(actions, modified, direction);
348
349     if (!d->actions)
350         return;
351
352     QSet<QAbstractAnimation*> anims;
353     for (int i = 0; i < d->actions->size(); i++) {
354         QSmoothedAnimation *ease;
355         bool needsRestart;
356         if (!d->activeAnimations.contains((*d->actions)[i].property)) {
357             ease = new QSmoothedAnimation();
358             d->wrapperGroup->addAnimation(ease);
359             d->activeAnimations.insert((*d->actions)[i].property, ease);
360             needsRestart = false;
361         } else {
362             ease = d->activeAnimations.value((*d->actions)[i].property);
363             needsRestart = true;
364         }
365         ease->target = (*d->actions)[i].property;
366         ease->to = (*d->actions)[i].toValue.toReal();
367
368         // copying public members from main value holder animation
369         ease->maximumEasingTime = d->anim->maximumEasingTime;
370         ease->reversingMode = d->anim->reversingMode;
371         ease->velocity = d->anim->velocity;
372         ease->userDuration = d->anim->userDuration;
373
374         ease->initialVelocity = ease->trackVelocity;
375
376         if (needsRestart)
377             ease->init();
378         anims.insert(ease);
379     }
380
381     for (int i = d->wrapperGroup->animationCount() - 1; i >= 0 ; --i) {
382         if (!anims.contains(d->wrapperGroup->animationAt(i))) {
383             QSmoothedAnimation *ease = static_cast<QSmoothedAnimation*>(d->wrapperGroup->animationAt(i));
384             d->activeAnimations.remove(ease->target);
385             d->wrapperGroup->takeAnimation(i);
386             delete ease;
387         }
388     }
389 }
390
391 /*!
392     \qmlproperty enumeration QtQuick2::SmoothedAnimation::reversingMode
393
394     Sets how the SmoothedAnimation behaves if an animation direction is reversed.
395
396     Possible values are:
397
398     \list
399     \o SmoothedAnimation.Eased (default) - the animation will smoothly decelerate, and then reverse direction
400     \o SmoothedAnimation.Immediate - the animation will immediately begin accelerating in the reverse direction, beginning with a velocity of 0
401     \o SmoothedAnimation.Sync - the property is immediately set to the target value
402     \endlist
403 */
404 QDeclarativeSmoothedAnimation::ReversingMode QDeclarativeSmoothedAnimation::reversingMode() const
405 {
406     Q_D(const QDeclarativeSmoothedAnimation);
407     return (QDeclarativeSmoothedAnimation::ReversingMode) d->anim->reversingMode;
408 }
409
410 void QDeclarativeSmoothedAnimation::setReversingMode(ReversingMode m)
411 {
412     Q_D(QDeclarativeSmoothedAnimation);
413     if (d->anim->reversingMode == m)
414         return;
415
416     d->anim->reversingMode = m;
417     emit reversingModeChanged();
418     d->updateRunningAnimations();
419 }
420
421 /*!
422     \qmlproperty int QtQuick2::SmoothedAnimation::duration
423
424     This property holds the animation duration, in msecs, used when tracking the source.
425
426     Setting this to -1 (the default) disables the duration value.
427
428     If the velocity value and the duration value are both enabled, then the animation will
429     use whichever gives the shorter duration.
430 */
431 int QDeclarativeSmoothedAnimation::duration() const
432 {
433     Q_D(const QDeclarativeSmoothedAnimation);
434     return d->anim->userDuration;
435 }
436
437 void QDeclarativeSmoothedAnimation::setDuration(int duration)
438 {
439     Q_D(QDeclarativeSmoothedAnimation);
440     if (duration != -1)
441         QDeclarativeNumberAnimation::setDuration(duration);
442     if(duration == d->anim->userDuration)
443         return;
444     d->anim->userDuration = duration;
445     d->updateRunningAnimations();
446 }
447
448 qreal QDeclarativeSmoothedAnimation::velocity() const
449 {
450     Q_D(const QDeclarativeSmoothedAnimation);
451     return d->anim->velocity;
452 }
453
454 /*!
455     \qmlproperty real QtQuick2::SmoothedAnimation::velocity
456
457     This property holds the average velocity allowed when tracking the 'to' value.
458
459     The default velocity of SmoothedAnimation is 200 units/second.
460
461     Setting this to -1 disables the velocity value.
462
463     If the velocity value and the duration value are both enabled, then the animation will
464     use whichever gives the shorter duration.
465 */
466 void QDeclarativeSmoothedAnimation::setVelocity(qreal v)
467 {
468     Q_D(QDeclarativeSmoothedAnimation);
469     if (d->anim->velocity == v)
470         return;
471
472     d->anim->velocity = v;
473     emit velocityChanged();
474     d->updateRunningAnimations();
475 }
476
477 /*!
478     \qmlproperty int QtQuick2::SmoothedAnimation::maximumEasingTime
479
480     This property specifies the maximum time, in msecs, any "eases" during the follow should take.
481     Setting this property causes the velocity to "level out" after at a time.  Setting
482     a negative value reverts to the normal mode of easing over the entire animation
483     duration.
484
485     The default value is -1.
486 */
487 int QDeclarativeSmoothedAnimation::maximumEasingTime() const
488 {
489     Q_D(const QDeclarativeSmoothedAnimation);
490     return d->anim->maximumEasingTime;
491 }
492
493 void QDeclarativeSmoothedAnimation::setMaximumEasingTime(int v)
494 {
495     Q_D(QDeclarativeSmoothedAnimation);
496     if(v == d->anim->maximumEasingTime)
497         return;
498     d->anim->maximumEasingTime = v;
499     emit maximumEasingTimeChanged();
500     d->updateRunningAnimations();
501 }
502
503 QT_END_NAMESPACE