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