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