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