src/declarative/util/qdeclarativespringfollow.cpp
branchGCC_SURGE
changeset 31 5daf16870df6
parent 30 5dc02b23752f
equal deleted inserted replaced
27:93b982ccede2 31:5daf16870df6
       
     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/qdeclarativespringfollow_p.h"
       
    43 
       
    44 #include "private/qdeclarativeanimation_p_p.h"
       
    45 
       
    46 #include <QtCore/qdebug.h>
       
    47 
       
    48 #include <private/qobject_p.h>
       
    49 
       
    50 #include <limits.h>
       
    51 #include <math.h>
       
    52 
       
    53 QT_BEGIN_NAMESPACE
       
    54 
       
    55 
       
    56 
       
    57 class QDeclarativeSpringFollowPrivate : public QObjectPrivate
       
    58 {
       
    59     Q_DECLARE_PUBLIC(QDeclarativeSpringFollow)
       
    60 public:
       
    61     QDeclarativeSpringFollowPrivate()
       
    62         : currentValue(0), to(0), maxVelocity(0), lastTime(0)
       
    63         , mass(1.0), spring(0.), damping(0.), velocity(0), epsilon(0.01)
       
    64         , modulus(0.0), useMass(false), haveModulus(false), enabled(true), mode(Track), clock(this) {}
       
    65 
       
    66     QDeclarativeProperty property;
       
    67     qreal currentValue;
       
    68     qreal to;
       
    69     qreal maxVelocity;
       
    70     qreal velocityms;
       
    71     int lastTime;
       
    72     qreal mass;
       
    73     qreal spring;
       
    74     qreal damping;
       
    75     qreal velocity;
       
    76     qreal epsilon;
       
    77     qreal modulus;
       
    78 
       
    79     bool useMass : 1;
       
    80     bool haveModulus : 1;
       
    81     bool enabled : 1;
       
    82 
       
    83     enum Mode {
       
    84         Track,
       
    85         Velocity,
       
    86         Spring
       
    87     };
       
    88     Mode mode;
       
    89 
       
    90     void tick(int);
       
    91     void updateMode();
       
    92     void start();
       
    93     void stop();
       
    94 
       
    95     QTickAnimationProxy<QDeclarativeSpringFollowPrivate, &QDeclarativeSpringFollowPrivate::tick> clock;
       
    96 };
       
    97 
       
    98 void QDeclarativeSpringFollowPrivate::tick(int time)
       
    99 {
       
   100     Q_Q(QDeclarativeSpringFollow);
       
   101 
       
   102     int elapsed = time - lastTime;
       
   103     if (!elapsed)
       
   104         return;
       
   105     qreal srcVal = to;
       
   106     if (haveModulus) {
       
   107         currentValue = fmod(currentValue, modulus);
       
   108         srcVal = fmod(srcVal, modulus);
       
   109     }
       
   110     if (mode == Spring) {
       
   111         if (elapsed < 16) // capped at 62fps.
       
   112             return;
       
   113         // Real men solve the spring DEs using RK4.
       
   114         // We'll do something much simpler which gives a result that looks fine.
       
   115         int count = elapsed / 16;
       
   116         for (int i = 0; i < count; ++i) {
       
   117             qreal diff = srcVal - currentValue;
       
   118             if (haveModulus && qAbs(diff) > modulus / 2) {
       
   119                 if (diff < 0)
       
   120                     diff += modulus;
       
   121                 else
       
   122                     diff -= modulus;
       
   123             }
       
   124             if (useMass)
       
   125                 velocity = velocity + (spring * diff - damping * velocity) / mass;
       
   126             else
       
   127                 velocity = velocity + spring * diff - damping * velocity;
       
   128             if (maxVelocity > 0.) {
       
   129                 // limit velocity
       
   130                 if (velocity > maxVelocity)
       
   131                     velocity = maxVelocity;
       
   132                 else if (velocity < -maxVelocity)
       
   133                     velocity = -maxVelocity;
       
   134             }
       
   135             currentValue += velocity * 16.0 / 1000.0;
       
   136             if (haveModulus) {
       
   137                 currentValue = fmod(currentValue, modulus);
       
   138                 if (currentValue < 0.0)
       
   139                     currentValue += modulus;
       
   140             }
       
   141         }
       
   142         if (qAbs(velocity) < epsilon && qAbs(srcVal - currentValue) < epsilon) {
       
   143             velocity = 0.0;
       
   144             currentValue = srcVal;
       
   145             clock.stop();
       
   146         }
       
   147         lastTime = time - (elapsed - count * 16);
       
   148     } else {
       
   149         qreal moveBy = elapsed * velocityms;
       
   150         qreal diff = srcVal - currentValue;
       
   151         if (haveModulus && qAbs(diff) > modulus / 2) {
       
   152             if (diff < 0)
       
   153                 diff += modulus;
       
   154             else
       
   155                 diff -= modulus;
       
   156         }
       
   157         if (diff > 0) {
       
   158             currentValue += moveBy;
       
   159             if (haveModulus)
       
   160                 currentValue = fmod(currentValue, modulus);
       
   161             if (currentValue > to) {
       
   162                 currentValue = to;
       
   163                 clock.stop();
       
   164             }
       
   165         } else {
       
   166             currentValue -= moveBy;
       
   167             if (haveModulus && currentValue < 0.0)
       
   168                 currentValue = fmod(currentValue, modulus) + modulus;
       
   169             if (currentValue < to) {
       
   170                 currentValue = to;
       
   171                 clock.stop();
       
   172             }
       
   173         }
       
   174         lastTime = time;
       
   175     }
       
   176     property.write(currentValue);
       
   177     emit q->valueChanged(currentValue);
       
   178     if (clock.state() != QAbstractAnimation::Running)
       
   179         emit q->syncChanged();
       
   180 }
       
   181 
       
   182 void QDeclarativeSpringFollowPrivate::updateMode()
       
   183 {
       
   184     if (spring == 0. && maxVelocity == 0.)
       
   185         mode = Track;
       
   186     else if (spring > 0.)
       
   187         mode = Spring;
       
   188     else
       
   189         mode = Velocity;
       
   190 }
       
   191 
       
   192 void QDeclarativeSpringFollowPrivate::start()
       
   193 {
       
   194     if (!enabled)
       
   195         return;
       
   196 
       
   197     Q_Q(QDeclarativeSpringFollow);
       
   198     if (mode == QDeclarativeSpringFollowPrivate::Track) {
       
   199         currentValue = to;
       
   200         property.write(currentValue);
       
   201     } else if (to != currentValue && clock.state() != QAbstractAnimation::Running) {
       
   202         lastTime = 0;
       
   203         currentValue = property.read().toReal();
       
   204         clock.start(); // infinity??
       
   205         emit q->syncChanged();
       
   206     }
       
   207 }
       
   208 
       
   209 void QDeclarativeSpringFollowPrivate::stop()
       
   210 {
       
   211     clock.stop();
       
   212 }
       
   213 
       
   214 /*!
       
   215     \qmlclass SpringFollow QDeclarativeSpringFollow
       
   216     \since 4.7
       
   217     \brief The SpringFollow element allows a property to track a value.
       
   218 
       
   219     In example below, \e rect2 will follow \e rect1 moving with a velocity of up to 200:
       
   220     \code
       
   221     Rectangle {
       
   222         id: rect1
       
   223         width: 20; height: 20
       
   224         color: "#00ff00"
       
   225         y: 200  // initial value
       
   226         SequentialAnimation on y {
       
   227             loops: Animation.Infinite
       
   228             NumberAnimation {
       
   229                 to: 200
       
   230                 easing.type: Easing.OutBounce
       
   231                 easing.amplitude: 100
       
   232                 duration: 2000
       
   233             }
       
   234             PauseAnimation { duration: 1000 }
       
   235         }
       
   236     }
       
   237     Rectangle {
       
   238         id: rect2
       
   239         x: rect1.width
       
   240         width: 20; height: 20
       
   241         color: "#ff0000"
       
   242         SpringFollow on y { to: rect1.y; velocity: 200 }
       
   243     }
       
   244     \endcode
       
   245 */
       
   246 
       
   247 QDeclarativeSpringFollow::QDeclarativeSpringFollow(QObject *parent)
       
   248 : QObject(*(new QDeclarativeSpringFollowPrivate),parent)
       
   249 {
       
   250 }
       
   251 
       
   252 QDeclarativeSpringFollow::~QDeclarativeSpringFollow()
       
   253 {
       
   254 }
       
   255 
       
   256 void QDeclarativeSpringFollow::setTarget(const QDeclarativeProperty &property)
       
   257 {
       
   258     Q_D(QDeclarativeSpringFollow);
       
   259     d->property = property;
       
   260     d->currentValue = property.read().toReal();
       
   261 }
       
   262 
       
   263 qreal QDeclarativeSpringFollow::to() const
       
   264 {
       
   265     Q_D(const QDeclarativeSpringFollow);
       
   266     return d->to;
       
   267 }
       
   268 
       
   269 /*!
       
   270     \qmlproperty real SpringFollow::to
       
   271     This property holds the target value which will be tracked.
       
   272 
       
   273     Bind to a property in order to track its changes.
       
   274 */
       
   275 
       
   276 void QDeclarativeSpringFollow::setTo(qreal value)
       
   277 {
       
   278     Q_D(QDeclarativeSpringFollow);
       
   279     if (d->clock.state() == QAbstractAnimation::Running && d->to == value)
       
   280         return;
       
   281 
       
   282     d->to = value;
       
   283     d->start();
       
   284 }
       
   285 
       
   286 /*!
       
   287     \qmlproperty real SpringFollow::velocity
       
   288     This property holds the maximum velocity allowed when tracking the source.
       
   289 */
       
   290 
       
   291 qreal QDeclarativeSpringFollow::velocity() const
       
   292 {
       
   293     Q_D(const QDeclarativeSpringFollow);
       
   294     return d->maxVelocity;
       
   295 }
       
   296 
       
   297 void QDeclarativeSpringFollow::setVelocity(qreal velocity)
       
   298 {
       
   299     Q_D(QDeclarativeSpringFollow);
       
   300     d->maxVelocity = velocity;
       
   301     d->velocityms = velocity / 1000.0;
       
   302     d->updateMode();
       
   303 }
       
   304 
       
   305 /*!
       
   306     \qmlproperty real SpringFollow::spring
       
   307     This property holds the spring constant
       
   308 
       
   309     The spring constant describes how strongly the target is pulled towards the
       
   310     source. Setting spring to 0 turns off spring tracking.  Useful values 0 - 5.0
       
   311 
       
   312     When a spring constant is set and the velocity property is greater than 0,
       
   313     velocity limits the maximum speed.
       
   314 */
       
   315 qreal QDeclarativeSpringFollow::spring() const
       
   316 {
       
   317     Q_D(const QDeclarativeSpringFollow);
       
   318     return d->spring;
       
   319 }
       
   320 
       
   321 void QDeclarativeSpringFollow::setSpring(qreal spring)
       
   322 {
       
   323     Q_D(QDeclarativeSpringFollow);
       
   324     d->spring = spring;
       
   325     d->updateMode();
       
   326 }
       
   327 
       
   328 /*!
       
   329     \qmlproperty real SpringFollow::damping
       
   330     This property holds the spring damping constant
       
   331 
       
   332     The damping constant describes how quickly a sprung follower comes to rest.
       
   333     Useful range is 0 - 1.0
       
   334 */
       
   335 qreal QDeclarativeSpringFollow::damping() const
       
   336 {
       
   337     Q_D(const QDeclarativeSpringFollow);
       
   338     return d->damping;
       
   339 }
       
   340 
       
   341 void QDeclarativeSpringFollow::setDamping(qreal damping)
       
   342 {
       
   343     Q_D(QDeclarativeSpringFollow);
       
   344     if (damping > 1.)
       
   345         damping = 1.;
       
   346 
       
   347     d->damping = damping;
       
   348 }
       
   349 
       
   350 
       
   351 /*!
       
   352     \qmlproperty real SpringFollow::epsilon
       
   353     This property holds the spring epsilon
       
   354 
       
   355     The epsilon is the rate and amount of change in the value which is close enough
       
   356     to 0 to be considered equal to zero. This will depend on the usage of the value.
       
   357     For pixel positions, 0.25 would suffice. For scale, 0.005 will suffice.
       
   358 
       
   359     The default is 0.01. Tuning this value can provide small performance improvements.
       
   360 */
       
   361 qreal QDeclarativeSpringFollow::epsilon() const
       
   362 {
       
   363     Q_D(const QDeclarativeSpringFollow);
       
   364     return d->epsilon;
       
   365 }
       
   366 
       
   367 void QDeclarativeSpringFollow::setEpsilon(qreal epsilon)
       
   368 {
       
   369     Q_D(QDeclarativeSpringFollow);
       
   370     d->epsilon = epsilon;
       
   371 }
       
   372 
       
   373 /*!
       
   374     \qmlproperty real SpringFollow::modulus
       
   375     This property holds the modulus value.
       
   376 
       
   377     Setting a \a modulus forces the target value to "wrap around" at the modulus.
       
   378     For example, setting the modulus to 360 will cause a value of 370 to wrap around to 10.
       
   379 */
       
   380 qreal QDeclarativeSpringFollow::modulus() const
       
   381 {
       
   382     Q_D(const QDeclarativeSpringFollow);
       
   383     return d->modulus;
       
   384 }
       
   385 
       
   386 void QDeclarativeSpringFollow::setModulus(qreal modulus)
       
   387 {
       
   388     Q_D(QDeclarativeSpringFollow);
       
   389     if (d->modulus != modulus) {
       
   390         d->haveModulus = modulus != 0.0;
       
   391         d->modulus = modulus;
       
   392         emit modulusChanged();
       
   393     }
       
   394 }
       
   395 
       
   396 /*!
       
   397     \qmlproperty real SpringFollow::mass
       
   398     This property holds the "mass" of the property being moved.
       
   399 
       
   400     mass is 1.0 by default.  Setting a different mass changes the dynamics of
       
   401     a \l spring follow.
       
   402 */
       
   403 qreal QDeclarativeSpringFollow::mass() const
       
   404 {
       
   405     Q_D(const QDeclarativeSpringFollow);
       
   406     return d->mass;
       
   407 }
       
   408 
       
   409 void QDeclarativeSpringFollow::setMass(qreal mass)
       
   410 {
       
   411     Q_D(QDeclarativeSpringFollow);
       
   412     if (d->mass != mass && mass > 0.0) {
       
   413         d->useMass = mass != 1.0;
       
   414         d->mass = mass;
       
   415         emit massChanged();
       
   416     }
       
   417 }
       
   418 
       
   419 /*!
       
   420     \qmlproperty bool SpringFollow::enabled
       
   421     This property holds whether the target will track the source.
       
   422 
       
   423     The default value of this property is 'true'.
       
   424 */
       
   425 bool QDeclarativeSpringFollow::enabled() const
       
   426 {
       
   427     Q_D(const QDeclarativeSpringFollow);
       
   428     return d->enabled;
       
   429 }
       
   430 
       
   431 void QDeclarativeSpringFollow::setEnabled(bool enabled)
       
   432 {
       
   433     Q_D(QDeclarativeSpringFollow);
       
   434     d->enabled = enabled;
       
   435     if (enabled)
       
   436         d->start();
       
   437     else
       
   438         d->stop();
       
   439 }
       
   440 
       
   441 /*!
       
   442     \qmlproperty bool SpringFollow::inSync
       
   443     This property is true when target is equal to the source; otherwise
       
   444     false.  If inSync is true the target is not being animated.
       
   445 
       
   446     If \l enabled is false then inSync will also be false.
       
   447 */
       
   448 bool QDeclarativeSpringFollow::inSync() const
       
   449 {
       
   450     Q_D(const QDeclarativeSpringFollow);
       
   451     return d->enabled && d->clock.state() != QAbstractAnimation::Running;
       
   452 }
       
   453 
       
   454 /*!
       
   455     \qmlproperty real SpringFollow::value
       
   456     The current value.
       
   457 */
       
   458 qreal QDeclarativeSpringFollow::value() const
       
   459 {
       
   460     Q_D(const QDeclarativeSpringFollow);
       
   461     return d->currentValue;
       
   462 }
       
   463 
       
   464 QT_END_NAMESPACE