source: trunk/src/declarative/util/qdeclarativespringanimation.cpp@ 986

Last change on this file since 986 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 14.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtDeclarative module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "private/qdeclarativespringanimation_p.h"
43
44#include "private/qdeclarativeanimation_p_p.h"
45#include <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
54QT_BEGIN_NAMESPACE
55
56
57class QDeclarativeSpringAnimationPrivate : public QDeclarativePropertyAnimationPrivate
58{
59 Q_DECLARE_PUBLIC(QDeclarativeSpringAnimation)
60public:
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
108void 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
138bool 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
216void 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 \ingroup qml-animation-transition
239 \inherits Animation
240 \since 4.7
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} documentation shows a variety of methods
259 for creating animations.
260
261 \sa SmoothedAnimation, {QML Animation}, {declarative/animation/basics}{Animation basics example}, {declarative/toys/clocks}{Clocks example}
262*/
263
264QDeclarativeSpringAnimation::QDeclarativeSpringAnimation(QObject *parent)
265: QDeclarativeNumberAnimation(*(new QDeclarativeSpringAnimationPrivate),parent)
266{
267 Q_D(QDeclarativeSpringAnimation);
268 d->clock = new QDeclarativeSpringAnimationPrivate::Clock(d, this);
269}
270
271QDeclarativeSpringAnimation::~QDeclarativeSpringAnimation()
272{
273}
274
275/*!
276 \qmlproperty real 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
283qreal QDeclarativeSpringAnimation::velocity() const
284{
285 Q_D(const QDeclarativeSpringAnimation);
286 return d->maxVelocity;
287}
288
289void 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 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*/
308qreal QDeclarativeSpringAnimation::spring() const
309{
310 Q_D(const QDeclarativeSpringAnimation);
311 return d->spring;
312}
313
314void QDeclarativeSpringAnimation::setSpring(qreal spring)
315{
316 Q_D(QDeclarativeSpringAnimation);
317 d->spring = spring;
318 d->updateMode();
319}
320
321/*!
322 \qmlproperty real 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*/
331qreal QDeclarativeSpringAnimation::damping() const
332{
333 Q_D(const QDeclarativeSpringAnimation);
334 return d->damping;
335}
336
337void 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 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*/
357qreal QDeclarativeSpringAnimation::epsilon() const
358{
359 Q_D(const QDeclarativeSpringAnimation);
360 return d->epsilon;
361}
362
363void QDeclarativeSpringAnimation::setEpsilon(qreal epsilon)
364{
365 Q_D(QDeclarativeSpringAnimation);
366 d->epsilon = epsilon;
367}
368
369/*!
370 \qmlproperty real 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*/
376qreal QDeclarativeSpringAnimation::modulus() const
377{
378 Q_D(const QDeclarativeSpringAnimation);
379 return d->modulus;
380}
381
382void 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 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*/
402qreal QDeclarativeSpringAnimation::mass() const
403{
404 Q_D(const QDeclarativeSpringAnimation);
405 return d->mass;
406}
407
408void 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
418void 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
456QAbstractAnimation *QDeclarativeSpringAnimation::qtAnimation()
457{
458 Q_D(QDeclarativeSpringAnimation);
459 return d->clock;
460}
461
462QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.