summaryrefslogtreecommitdiffstats
path: root/src/declarative/util/qdeclarativeeasefollow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/declarative/util/qdeclarativeeasefollow.cpp')
-rw-r--r--src/declarative/util/qdeclarativeeasefollow.cpp536
1 files changed, 536 insertions, 0 deletions
diff --git a/src/declarative/util/qdeclarativeeasefollow.cpp b/src/declarative/util/qdeclarativeeasefollow.cpp
new file mode 100644
index 0000000..0430a0b
--- /dev/null
+++ b/src/declarative/util/qdeclarativeeasefollow.cpp
@@ -0,0 +1,536 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdeclarativeeasefollow_p.h"
+
+#include "qdeclarativeanimation_p_p.h"
+
+#include <qdeclarativemetaproperty.h>
+
+#include <QtCore/qdebug.h>
+
+#include <math.h>
+
+QT_BEGIN_NAMESPACE
+
+
+
+class QDeclarativeEaseFollowPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QDeclarativeEaseFollow)
+public:
+ QDeclarativeEaseFollowPrivate()
+ : source(0), velocity(200), duration(-1), maximumEasingTime(-1),
+ reversingMode(QDeclarativeEaseFollow::Eased), initialVelocity(0),
+ initialValue(0), invert(false), enabled(true), trackVelocity(0), clockOffset(0),
+ lastTick(0), clock(this)
+ {}
+
+ qreal source;
+ qreal velocity;
+ qreal duration;
+ qreal maximumEasingTime;
+ QDeclarativeEaseFollow::ReversingMode reversingMode;
+
+ qreal initialVelocity;
+ qreal initialValue;
+ bool invert;
+ bool enabled;
+
+ qreal trackVelocity;
+
+ QDeclarativeMetaProperty target;
+
+ int clockOffset;
+ int lastTick;
+ void tick(int);
+ void clockStart();
+ void clockStop();
+ QTickAnimationProxy<QDeclarativeEaseFollowPrivate, &QDeclarativeEaseFollowPrivate::tick> clock;
+
+ void restart();
+
+ // Parameters for use in tick()
+ qreal a; // Acceleration
+ qreal d; // Deceleration
+ qreal tf; // Total time
+ qreal tp; // Time at which peak velocity occurs
+ qreal td; // Time at which decelleration begins
+ qreal vp; // Velocity at tp
+ qreal sp; // Displacement at tp
+ qreal sd; // Displacement at td
+ qreal vi; // "Normalized" initialvelocity
+ bool recalc();
+};
+
+bool QDeclarativeEaseFollowPrivate::recalc()
+{
+ qreal s = source - initialValue;
+ vi = initialVelocity;
+
+ s = (invert?-1.0:1.0) * s;
+ vi = (invert?-1.0:1.0) * vi;
+
+ if (duration > 0 && velocity > 0) {
+ tf = s / velocity;
+ if (tf > (duration / 1000.)) tf = (duration / 1000.);
+ } else if (duration > 0) {
+ tf = duration / 1000.;
+ } else if (velocity > 0) {
+ tf = s / velocity;
+ } else {
+ return false;
+ }
+
+ if (maximumEasingTime == 0) {
+ a = 0;
+ d = 0;
+ tp = 0;
+ td = tf;
+ vp = velocity;
+ sp = 0;
+ sd = s;
+ } else if (maximumEasingTime != -1 && tf > (maximumEasingTime / 1000.)) {
+
+ qreal met = maximumEasingTime / 1000.;
+ td = tf - met;
+
+ qreal c1 = td;
+ qreal c2 = (tf - td) * vi - tf * velocity;
+ qreal c3 = -0.5 * (tf - td) * vi * vi;
+
+ qreal vp1 = (-c2 + sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1);
+ // qreal vp2 = (-c2 - sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1);
+
+ vp = vp1;
+ a = vp / met;
+ d = a;
+ tp = (vp - vi) / a;
+ sp = vi * tp + 0.5 * a * tp * tp;
+ sd = sp + (td - tp) * vp;
+ } else {
+
+ qreal c1 = 0.25 * tf * tf;
+ qreal c2 = 0.5 * vi * tf - s;
+ qreal c3 = -0.25 * vi * vi;
+
+ qreal a1 = (-c2 + sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1);
+ //qreal a2 = (-c2 - sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1);
+
+ qreal tp1 = 0.5 * tf - 0.5 * vi / a1;
+ //qreal tp2 = 0.5 * tf - 0.5 * vi / a2;
+ qreal vp1 = a1 * tp1 + vi;
+ //qreal vp2 = a2 * tp2 + vi;
+
+ qreal sp1 = 0.5 * a1 * tp1 * tp1 + vi * tp1;
+ //qreal sp2 = 0.5 * a2 * tp2 * tp2 + vi * tp2;
+
+ a = a1;
+ d = a1;
+ tp = tp1;
+ td = tp1;
+ vp = vp1;
+ sp = sp1;
+ sd = sp1;
+ }
+
+ /*
+ qWarning() << "a:" << a << "tf:" << tf << "tp:" << tp << "vp:"
+ << vp << "sp:" << sp << "vi:" << vi << "invert:" << invert;
+ */
+ return true;
+}
+
+void QDeclarativeEaseFollowPrivate::clockStart()
+{
+ if (clock.state() == QAbstractAnimation::Running) {
+ clockOffset = lastTick;
+ return;
+ } else {
+ clockOffset = 0;
+ lastTick = 0;
+ clock.start();
+ }
+}
+
+void QDeclarativeEaseFollowPrivate::clockStop()
+{
+ clockOffset = 0;
+ lastTick = 0;
+ clock.stop();
+}
+
+void QDeclarativeEaseFollowPrivate::tick(int t)
+{
+ lastTick = t;
+ t -= clockOffset;
+
+ qreal time_seconds = qreal(t) / 1000.;
+
+ qreal out = 0;
+ if (time_seconds < tp) {
+
+ trackVelocity = vi + time_seconds * a;
+ trackVelocity = (invert?-1.0:1.0) * trackVelocity;
+
+ qreal value = 0.5 * a * time_seconds * time_seconds + vi * time_seconds;
+ value = (invert?-1.0:1.0) * value;
+ target.write(initialValue + value);
+ out = initialValue + value;
+ } else if (time_seconds < td) {
+
+ time_seconds -= tp;
+ trackVelocity = (invert?-1.0:1.0) * vp;
+ qreal value = sp + time_seconds * vp;
+ value = (invert?-1.0:1.0) * value;
+
+ target.write(initialValue + value);
+
+ out = initialValue + value;
+ } else if (time_seconds < tf) {
+
+ time_seconds -= td;
+
+ trackVelocity = vp - time_seconds * a;
+ trackVelocity = (invert?-1.0:1.0) * trackVelocity;
+
+ qreal value = sd - 0.5 * d * time_seconds * time_seconds + vp * time_seconds;
+ value = (invert?-1.0:1.0) * value;
+
+ target.write(initialValue + value);
+
+ out = initialValue + value;
+ } else {
+
+ clock.stop();
+
+ trackVelocity = 0;
+ target.write(source);
+ }
+
+ //qWarning() << out << trackVelocity << t << a;
+}
+
+/*!
+ \qmlclass EaseFollow QDeclarativeEaseFollow
+ \brief The EaseFollow element allows a property to smoothly track a value.
+
+ The EaseFollow smoothly animates a property's value to a set target value
+ using an ease in/out quad easing curve. If the target value changes while
+ the animation is in progress, the easing curves used to animate to the old
+ and the new target values are spliced together to avoid any obvious visual
+ glitches.
+
+ The property animation is configured by setting the velocity at which the
+ animation should occur, or the duration that the animation should take.
+ If both a velocity and a duration are specified, the one that results in
+ the quickest animation is chosen for each change in the target value.
+
+ For example, animating from 0 to 800 will take 4 seconds if a velocity
+ of 200 is set, will take 8 seconds with a duration of 8000 set, and will
+ take 4 seconds with both a velocity of 200 and a duration of 8000 set.
+ Animating from 0 to 20000 will take 10 seconds if a velocity of 200 is set,
+ will take 8 seconds with a duration of 8000 set, and will take 8 seconds
+ with both a velocity of 200 and a duration of 8000 set.
+
+ The follow example shows one rectangle tracking the position of another.
+\code
+import Qt 4.6
+
+Rectangle {
+ width: 800; height: 600; color: "blue"
+
+ Rectangle {
+ color: "green"
+ width: 60; height: 60;
+ x: -5; y: -5;
+ x: EaseFollow { source: rect1.x - 5; velocity: 200 }
+ y: EaseFollow { source: rect1.y - 5; velocity: 200 }
+ }
+
+ Rectangle {
+ id: rect1
+ color: "red"
+ width: 50; height: 50;
+ }
+
+ focus: true
+ Keys.onRightPressed: rect1.x = rect1.x + 100
+ Keys.onLeftPressed: rect1.x = rect1.x - 100
+ Keys.onUpPressed: rect1.y = rect1.y - 100
+ Keys.onDownPressed: rect1.y = rect1.y + 100
+}
+\endcode
+
+ The default velocity of EaseFollow is 200 units/second. Note that if the range of the
+ value being animated is small, then the velocity will need to be adjusted
+ appropriately. For example, the opacity of an item ranges from 0 - 1.0.
+ To enable a smooth animation in this range the velocity will need to be
+ set to a value such as 0.5 units/second. Animating from 0 to 1.0 with a velocity
+ of 0.5 will take 2000 ms to complete.
+
+ \sa SpringFollow
+*/
+
+QDeclarativeEaseFollow::QDeclarativeEaseFollow(QObject *parent)
+: QObject(*(new QDeclarativeEaseFollowPrivate), parent)
+{
+}
+
+QDeclarativeEaseFollow::~QDeclarativeEaseFollow()
+{
+}
+
+/*!
+ \qmlproperty qreal EaseFollow::source
+ This property holds the source value which will be tracked.
+
+ Bind to a property in order to track its changes.
+*/
+qreal QDeclarativeEaseFollow::sourceValue() const
+{
+ Q_D(const QDeclarativeEaseFollow);
+ return d->source;
+}
+
+/*!
+ \qmlproperty enumeration EaseFollow::reversingMode
+
+ Sets how the EaseFollow behaves if an animation direction is reversed.
+
+ If reversing mode is \c Eased, the animation will smoothly decelerate, and
+ then reverse direction. If the reversing mode is \c Immediate, the
+ animation will immediately begin accelerating in the reverse direction,
+ begining with a velocity of 0. If the reversing mode is \c Sync, the
+ property is immediately set to the target value.
+*/
+QDeclarativeEaseFollow::ReversingMode QDeclarativeEaseFollow::reversingMode() const
+{
+ Q_D(const QDeclarativeEaseFollow);
+ return d->reversingMode;
+}
+
+void QDeclarativeEaseFollow::setReversingMode(ReversingMode m)
+{
+ Q_D(QDeclarativeEaseFollow);
+ if (d->reversingMode == m)
+ return;
+
+ d->reversingMode = m;
+ emit reversingModeChanged();
+}
+
+void QDeclarativeEaseFollowPrivate::restart()
+{
+ if (!enabled || velocity == 0) {
+ clockStop();
+ return;
+ }
+
+ initialValue = target.read().toReal();
+
+ if (source == initialValue) {
+ clockStop();
+ return;
+ }
+
+ bool hasReversed = trackVelocity != 0. &&
+ ((trackVelocity > 0) == ((initialValue - source) > 0));
+
+ if (hasReversed) {
+ switch (reversingMode) {
+ default:
+ case QDeclarativeEaseFollow::Eased:
+ break;
+ case QDeclarativeEaseFollow::Sync:
+ target.write(source);
+ return;
+ case QDeclarativeEaseFollow::Immediate:
+ initialVelocity = 0;
+ clockStop();
+ break;
+ }
+ }
+
+ trackVelocity = initialVelocity;
+
+ invert = (source < initialValue);
+
+ if (!recalc()) {
+ target.write(source);
+ clockStop();
+ return;
+ }
+
+ clockStart();
+}
+
+void QDeclarativeEaseFollow::setSourceValue(qreal s)
+{
+ Q_D(QDeclarativeEaseFollow);
+
+ if (d->clock.state() == QAbstractAnimation::Running && d->source == s)
+ return;
+
+ d->source = s;
+ d->initialVelocity = d->trackVelocity;
+ d->restart();
+
+ emit sourceChanged();
+}
+
+/*!
+ \qmlproperty qreal EaseFollow::duration
+
+ This property holds the animation duration used when tracking the source.
+
+ Setting this to -1 (the default) disables the duration value.
+*/
+qreal QDeclarativeEaseFollow::duration() const
+{
+ Q_D(const QDeclarativeEaseFollow);
+ return d->duration;
+}
+
+void QDeclarativeEaseFollow::setDuration(qreal v)
+{
+ Q_D(QDeclarativeEaseFollow);
+ if (d->duration == v)
+ return;
+
+ d->duration = v;
+ d->trackVelocity = 0;
+
+ if (d->clock.state() == QAbstractAnimation::Running)
+ d->restart();
+
+ emit durationChanged();
+}
+
+qreal QDeclarativeEaseFollow::velocity() const
+{
+ Q_D(const QDeclarativeEaseFollow);
+ return d->velocity;
+}
+
+/*!
+ \qmlproperty qreal EaseFollow::velocity
+
+ This property holds the average velocity allowed when tracking the source.
+
+ The default velocity of EaseFollow is 200 units/second.
+
+ Setting this to -1 disables the velocity value.
+*/
+void QDeclarativeEaseFollow::setVelocity(qreal v)
+{
+ Q_D(QDeclarativeEaseFollow);
+ if (d->velocity == v)
+ return;
+
+ d->velocity = v;
+ d->trackVelocity = 0;
+
+ if (d->clock.state() == QAbstractAnimation::Running)
+ d->restart();
+
+ emit velocityChanged();
+}
+
+/*!
+ \qmlproperty bool EaseFollow::enabled
+ This property holds whether the target will track the source.
+*/
+bool QDeclarativeEaseFollow::enabled() const
+{
+ Q_D(const QDeclarativeEaseFollow);
+ return d->enabled;
+}
+
+void QDeclarativeEaseFollow::setEnabled(bool enabled)
+{
+ Q_D(QDeclarativeEaseFollow);
+ if (d->enabled == enabled)
+ return;
+
+ d->enabled = enabled;
+ if (enabled)
+ d->restart();
+ else
+ d->clockStop();
+
+ emit enabledChanged();
+}
+
+void QDeclarativeEaseFollow::setTarget(const QDeclarativeMetaProperty &t)
+{
+ Q_D(QDeclarativeEaseFollow);
+ d->target = t;
+}
+
+/*!
+\qmlproperty qreal EaseFollow::maximumEasingTime
+
+This property specifies the maximum time an "eases" during the follow should take.
+Setting this property causes the velocity to "level out" after at a time. Setting
+a negative value reverts to the normal mode of easing over the entire animation
+duration.
+
+The default value is -1.
+*/
+qreal QDeclarativeEaseFollow::maximumEasingTime() const
+{
+ Q_D(const QDeclarativeEaseFollow);
+ return d->maximumEasingTime;
+}
+
+void QDeclarativeEaseFollow::setMaximumEasingTime(qreal v)
+{
+ Q_D(QDeclarativeEaseFollow);
+ d->maximumEasingTime = v;
+
+ if (d->clock.state() == QAbstractAnimation::Running)
+ d->restart();
+
+ emit maximumEasingTimeChanged();
+}
+
+QT_END_NAMESPACE