Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / qtquick1 / util / qdeclarativespringanimation.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/qdeclarativespringanimation_p.h"
43
44 #include "QtQuick1/private/qdeclarativeanimation_p_p.h"
45 #include <QtDeclarative/private/qdeclarativeproperty_p.h>
46
47 #include <QtCore/qdebug.h>
48
49 #include <private/qobject_p.h>
50
51 #include <limits.h>
52 #include <math.h>
53
54 QT_BEGIN_NAMESPACE
55
56
57
58
59 class QDeclarative1SpringAnimationPrivate : public QDeclarative1PropertyAnimationPrivate
60 {
61     Q_DECLARE_PUBLIC(QDeclarative1SpringAnimation)
62 public:
63
64
65     struct SpringAnimation {
66         SpringAnimation()
67             : currentValue(0), to(0), velocity(0), start(0), duration(0) {}
68         qreal currentValue;
69         qreal to;
70         qreal velocity;
71         int start;
72         int duration;
73     };
74     QHash<QDeclarativeProperty, SpringAnimation> activeAnimations;
75
76     qreal maxVelocity;
77     qreal velocityms;
78     int lastTime;
79     qreal mass;
80     qreal spring;
81     qreal damping;
82     qreal epsilon;
83     qreal modulus;
84
85     bool useMass : 1;
86     bool haveModulus : 1;
87
88     enum Mode {
89         Track,
90         Velocity,
91         Spring
92     };
93     Mode mode;
94
95     QDeclarative1SpringAnimationPrivate()
96           : maxVelocity(0), velocityms(0), lastTime(0)
97           , mass(1.0), spring(0.), damping(0.), epsilon(0.01)
98           , modulus(0.0), useMass(false), haveModulus(false)
99           , mode(Track), clock(0)
100     { }
101
102     void tick(int time);
103     bool animate(const QDeclarativeProperty &property, SpringAnimation &animation, int elapsed);
104     void updateMode();
105
106     typedef QTickAnimationProxy_1<QDeclarative1SpringAnimationPrivate, &QDeclarative1SpringAnimationPrivate::tick> Clock;
107     Clock *clock;
108 };
109
110 void QDeclarative1SpringAnimationPrivate::tick(int time)
111 {
112     if (mode == Track) {
113         clock->stop();
114         return;
115     }
116     int elapsed = time - lastTime;
117     if (!elapsed)
118         return;
119
120     if (mode == Spring) {
121         if (elapsed < 16) // capped at 62fps.
122             return;
123         int count = elapsed / 16;
124         lastTime = time - (elapsed - count * 16);
125     } else {
126         lastTime = time;
127     }
128
129     QMutableHashIterator<QDeclarativeProperty, SpringAnimation> it(activeAnimations);
130     while (it.hasNext()) {
131         it.next();
132         if (animate(it.key(), it.value(), elapsed))
133             it.remove();
134     }
135
136     if (activeAnimations.isEmpty())
137         clock->stop();
138 }
139
140 bool QDeclarative1SpringAnimationPrivate::animate(const QDeclarativeProperty &property, SpringAnimation &animation, int elapsed)
141 {
142     qreal srcVal = animation.to;
143
144     bool stop = false;
145
146     if (haveModulus) {
147         animation.currentValue = fmod(animation.currentValue, modulus);
148         srcVal = fmod(srcVal, modulus);
149     }
150     if (mode == Spring) {
151         // Real men solve the spring DEs using RK4.
152         // We'll do something much simpler which gives a result that looks fine.
153         int count = elapsed / 16;
154         for (int i = 0; i < count; ++i) {
155             qreal diff = srcVal - animation.currentValue;
156             if (haveModulus && qAbs(diff) > modulus / 2) {
157                 if (diff < 0)
158                     diff += modulus;
159                 else
160                     diff -= modulus;
161             }
162             if (useMass)
163                 animation.velocity = animation.velocity + (spring * diff - damping * animation.velocity) / mass;
164             else
165                 animation.velocity = animation.velocity + spring * diff - damping * animation.velocity;
166             if (maxVelocity > 0.) {
167                 // limit velocity
168                 if (animation.velocity > maxVelocity)
169                     animation.velocity = maxVelocity;
170                 else if (animation.velocity < -maxVelocity)
171                     animation.velocity = -maxVelocity;
172             }
173             animation.currentValue += animation.velocity * 16.0 / 1000.0;
174             if (haveModulus) {
175                 animation.currentValue = fmod(animation.currentValue, modulus);
176                 if (animation.currentValue < 0.0)
177                     animation.currentValue += modulus;
178             }
179         }
180         if (qAbs(animation.velocity) < epsilon && qAbs(srcVal - animation.currentValue) < epsilon) {
181             animation.velocity = 0.0;
182             animation.currentValue = srcVal;
183             stop = true;
184         }
185     } else {
186         qreal moveBy = elapsed * velocityms;
187         qreal diff = srcVal - animation.currentValue;
188         if (haveModulus && qAbs(diff) > modulus / 2) {
189             if (diff < 0)
190                 diff += modulus;
191             else
192                 diff -= modulus;
193         }
194         if (diff > 0) {
195             animation.currentValue += moveBy;
196             if (haveModulus)
197                 animation.currentValue = fmod(animation.currentValue, modulus);
198         } else {
199             animation.currentValue -= moveBy;
200             if (haveModulus && animation.currentValue < 0.0)
201                 animation.currentValue = fmod(animation.currentValue, modulus) + modulus;
202         }
203         if (lastTime - animation.start >= animation.duration) {
204             animation.currentValue = animation.to;
205             stop = true;
206         }
207     }
208
209     qreal old_to = animation.to;
210
211     QDeclarativePropertyPrivate::write(property, animation.currentValue,
212                                        QDeclarativePropertyPrivate::BypassInterceptor |
213                                        QDeclarativePropertyPrivate::DontRemoveBinding);
214
215     return (stop && old_to == animation.to); // do not stop if we got restarted
216 }
217
218 void QDeclarative1SpringAnimationPrivate::updateMode()
219 {
220     if (spring == 0. && maxVelocity == 0.)
221         mode = Track;
222     else if (spring > 0.)
223         mode = Spring;
224     else {
225         mode = Velocity;
226         QHash<QDeclarativeProperty, SpringAnimation>::iterator it;
227         for (it = activeAnimations.begin(); it != activeAnimations.end(); ++it) {
228             SpringAnimation &animation = *it;
229             animation.start = lastTime;
230             qreal dist = qAbs(animation.currentValue - animation.to);
231             if (haveModulus && dist > modulus / 2)
232                 dist = modulus - fmod(dist, modulus);
233             animation.duration = dist / velocityms;
234         }
235     }
236 }
237
238 /*!
239     \qmlclass SpringAnimation QDeclarative1SpringAnimation
240     \inqmlmodule QtQuick 1
241     \ingroup qml-animation-transition
242     \inherits NumberAnimation
243     \since QtQuick 1.0
244
245     \brief The SpringAnimation element allows a property to track a value in a spring-like motion.
246
247     SpringAnimation mimics the oscillatory behavior of a spring, with the appropriate \l spring constant to
248     control the acceleration and the \l damping to control how quickly the effect dies away.
249
250     You can also limit the maximum \l velocity of the animation.
251
252     The following \l Rectangle moves to the position of the mouse using a
253     SpringAnimation when the mouse is clicked. The use of the \l Behavior
254     on the \c x and \c y values indicates that whenever these values are
255     changed, a SpringAnimation should be applied.
256
257     \snippet doc/src/snippets/qtquick1/springanimation.qml 0
258
259     Like any other animation element, a SpringAnimation can be applied in a
260     number of ways, including transitions, behaviors and property value
261     sources. The \l {QML Animation and Transitions} documentation shows a
262     variety of methods for creating animations.
263
264     \sa SmoothedAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example}, {declarative/toys/clocks}{Clocks example}
265 */
266
267 QDeclarative1SpringAnimation::QDeclarative1SpringAnimation(QObject *parent)
268 : QDeclarative1NumberAnimation(*(new QDeclarative1SpringAnimationPrivate),parent)
269 {
270     Q_D(QDeclarative1SpringAnimation);
271     d->clock = new QDeclarative1SpringAnimationPrivate::Clock(d, this);
272 }
273
274 QDeclarative1SpringAnimation::~QDeclarative1SpringAnimation()
275 {
276 }
277
278 /*!
279     \qmlproperty real QtQuick1::SpringAnimation::velocity
280
281     This property holds the maximum velocity allowed when tracking the source.
282
283     The default value is 0 (no maximum velocity).
284 */
285
286 qreal QDeclarative1SpringAnimation::velocity() const
287 {
288     Q_D(const QDeclarative1SpringAnimation);
289     return d->maxVelocity;
290 }
291
292 void QDeclarative1SpringAnimation::setVelocity(qreal velocity)
293 {
294     Q_D(QDeclarative1SpringAnimation);
295     d->maxVelocity = velocity;
296     d->velocityms = velocity / 1000.0;
297     d->updateMode();
298 }
299
300 /*!
301     \qmlproperty real QtQuick1::SpringAnimation::spring
302
303     This property describes how strongly the target is pulled towards the
304     source. The default value is 0 (that is, the spring-like motion is disabled).
305
306     The useful value range is 0 - 5.0.
307
308     When this property is set and the \l velocity value is greater than 0,
309     the \l velocity limits the maximum speed.
310 */
311 qreal QDeclarative1SpringAnimation::spring() const
312 {
313     Q_D(const QDeclarative1SpringAnimation);
314     return d->spring;
315 }
316
317 void QDeclarative1SpringAnimation::setSpring(qreal spring)
318 {
319     Q_D(QDeclarative1SpringAnimation);
320     d->spring = spring;
321     d->updateMode();
322 }
323
324 /*!
325     \qmlproperty real QtQuick1::SpringAnimation::damping
326     This property holds the spring damping value.
327
328     This parameter can be used to tweak the spring behaviour by exerting a resisting force
329     (like friction).
330
331     The default value is 0.
332
333     The useful value range is 0 - 1.0. The higher the value, the faster it
334     usually comes to rest.
335 */
336 qreal QDeclarative1SpringAnimation::damping() const
337 {
338     Q_D(const QDeclarative1SpringAnimation);
339     return d->damping;
340 }
341
342 void QDeclarative1SpringAnimation::setDamping(qreal damping)
343 {
344     Q_D(QDeclarative1SpringAnimation);
345     if (damping > 1.)
346         damping = 1.;
347
348     d->damping = damping;
349 }
350
351
352 /*!
353     \qmlproperty real QtQuick1::SpringAnimation::epsilon
354     This property holds the spring epsilon.
355
356     The epsilon is the rate and amount of change in the value which is close enough
357     to 0 to be considered equal to zero. This will depend on the usage of the value.
358     For pixel positions, 0.25 would suffice. For scale, 0.005 will suffice.
359
360     The default is 0.01. Tuning this value can provide small performance improvements.
361 */
362 qreal QDeclarative1SpringAnimation::epsilon() const
363 {
364     Q_D(const QDeclarative1SpringAnimation);
365     return d->epsilon;
366 }
367
368 void QDeclarative1SpringAnimation::setEpsilon(qreal epsilon)
369 {
370     Q_D(QDeclarative1SpringAnimation);
371     d->epsilon = epsilon;
372 }
373
374 /*!
375     \qmlproperty real QtQuick1::SpringAnimation::modulus
376     This property holds the modulus value. The default value is 0.
377
378     Setting a \a modulus forces the target value to "wrap around" at the modulus.
379     For example, setting the modulus to 360 will cause a value of 370 to wrap around to 10.
380 */
381 qreal QDeclarative1SpringAnimation::modulus() const
382 {
383     Q_D(const QDeclarative1SpringAnimation);
384     return d->modulus;
385 }
386
387 void QDeclarative1SpringAnimation::setModulus(qreal modulus)
388 {
389     Q_D(QDeclarative1SpringAnimation);
390     if (d->modulus != modulus) {
391         d->haveModulus = modulus != 0.0;
392         d->modulus = modulus;
393         d->updateMode();
394         emit modulusChanged();
395     }
396 }
397
398 /*!
399     \qmlproperty real QtQuick1::SpringAnimation::mass
400     This property holds the "mass" of the property being moved.
401
402     The value is 1.0 by default.
403
404     A greater mass causes slower movement and a greater spring-like
405     motion when an item comes to rest.
406 */
407 qreal QDeclarative1SpringAnimation::mass() const
408 {
409     Q_D(const QDeclarative1SpringAnimation);
410     return d->mass;
411 }
412
413 void QDeclarative1SpringAnimation::setMass(qreal mass)
414 {
415     Q_D(QDeclarative1SpringAnimation);
416     if (d->mass != mass && mass > 0.0) {
417         d->useMass = mass != 1.0;
418         d->mass = mass;
419         emit massChanged();
420     }
421 }
422
423 void QDeclarative1SpringAnimation::transition(QDeclarative1StateActions &actions,
424                                              QDeclarativeProperties &modified,
425                                              TransitionDirection direction)
426 {
427     Q_D(QDeclarative1SpringAnimation);
428     Q_UNUSED(direction);
429
430     if (d->clock->state() != QAbstractAnimation::Running) {
431         d->lastTime = 0;
432     }
433
434     QDeclarative1NumberAnimation::transition(actions, modified, direction);
435
436     if (!d->actions)
437         return;
438
439     if (!d->actions->isEmpty()) {
440         for (int i = 0; i < d->actions->size(); ++i) {
441             const QDeclarativeProperty &property = d->actions->at(i).property;
442             QDeclarative1SpringAnimationPrivate::SpringAnimation &animation
443                     = d->activeAnimations[property];
444             animation.to = d->actions->at(i).toValue.toReal();
445             animation.start = d->lastTime;
446             if (d->fromIsDefined)
447                 animation.currentValue = d->actions->at(i).fromValue.toReal();
448             else
449                 animation.currentValue = property.read().toReal();
450             if (d->mode == QDeclarative1SpringAnimationPrivate::Velocity) {
451                 qreal dist = qAbs(animation.currentValue - animation.to);
452                 if (d->haveModulus && dist > d->modulus / 2)
453                     dist = d->modulus - fmod(dist, d->modulus);
454                 animation.duration = dist / d->velocityms;
455             }
456         }
457     }
458 }
459
460
461 QAbstractAnimation *QDeclarative1SpringAnimation::qtAnimation()
462 {
463     Q_D(QDeclarative1SpringAnimation);
464     return d->clock;
465 }
466
467
468
469 QT_END_NAMESPACE