PathAnimation updates.
authorMichael Brasser <michael.brasser@nokia.com>
Wed, 7 Sep 2011 02:39:58 +0000 (12:39 +1000)
committerQt by Nokia <qt-info@nokia.com>
Sun, 18 Sep 2011 23:05:17 +0000 (01:05 +0200)
Allow smooth orientation changes, smooth interruptions,
and implicit "from" in PathAnimation.

Change-Id: I2191f6df89ec25d78b1d498827281803a07129c9
Reviewed-on: http://codereview.qt-project.org/4378
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Martin Jones <martin.jones@nokia.com>

src/declarative/items/qsganimation.cpp
src/declarative/items/qsganimation_p.h
src/declarative/items/qsganimation_p_p.h
src/declarative/util/qdeclarativeanimation_p_p.h
src/declarative/util/qdeclarativepath.cpp
src/declarative/util/qdeclarativepath_p.h
tests/auto/declarative/qdeclarativeanimations/data/pathAnimation2.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeanimations/data/pathAnimationNoStart.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeanimations/tst_qdeclarativeanimations.cpp

index eda5629..59c9fe3 100644 (file)
@@ -554,6 +554,53 @@ void QSGPathAnimation::setAnchorPoint(const QPointF &point)
     emit anchorPointChanged(point);
 }
 
+qreal QSGPathAnimation::orientationEntryInterval() const
+{
+    Q_D(const QSGPathAnimation);
+    return d->entryInterval;
+}
+
+void QSGPathAnimation::setOrientationEntryInterval(qreal interval)
+{
+    Q_D(QSGPathAnimation);
+    if (d->entryInterval == interval)
+        return;
+    d->entryInterval = interval;
+    emit orientationEntryIntervalChanged(interval);
+}
+
+qreal QSGPathAnimation::orientationExitInterval() const
+{
+    Q_D(const QSGPathAnimation);
+    return d->exitInterval;
+}
+
+void QSGPathAnimation::setOrientationExitInterval(qreal interval)
+{
+    Q_D(QSGPathAnimation);
+    if (d->exitInterval == interval)
+        return;
+    d->exitInterval = interval;
+    emit orientationExitIntervalChanged(interval);
+}
+
+qreal QSGPathAnimation::endRotation() const
+{
+    Q_D(const QSGPathAnimation);
+    return d->endRotation.isNull ? qreal(0) : d->endRotation.value;
+}
+
+void QSGPathAnimation::setEndRotation(qreal rotation)
+{
+    Q_D(QSGPathAnimation);
+    if (!d->endRotation.isNull && d->endRotation == rotation)
+        return;
+
+    d->endRotation = rotation;
+    emit endRotationChanged(d->endRotation);
+}
+
+
 QAbstractAnimation *QSGPathAnimation::qtAnimation()
 {
     Q_D(QSGPathAnimation);
@@ -569,9 +616,13 @@ void QSGPathAnimation::transition(QDeclarativeStateActions &actions,
 
     data->orientation = d->orientation;
     data->anchorPoint = d->anchorPoint;
+    data->entryInterval = d->entryInterval;
+    data->exitInterval = d->exitInterval;
+    data->endRotation = d->endRotation;
     data->reverse = direction == Backward ? true : false;
     data->fromSourced = false;
-    data->fromDefined = d->path ? !d->path->hasStartX() || !d->path->hasStartY() ? false : true : false;   //### handle x/y separately, as well as endpoint specification?
+    data->fromDefined = (d->path && d->path->hasStartX() && d->path->hasStartY()) ? true : false;
+    data->toDefined = d->path ? d->path->hasEnd() : false;
     int origModifiedSize = modified.count();
 
     for (int i = 0; i < actions.count(); ++i) {
@@ -591,7 +642,7 @@ void QSGPathAnimation::transition(QDeclarativeStateActions &actions,
     }
 
     if (d->target && d->path &&
-        (modified.count() > origModifiedSize || data->fromDefined)) {
+        (modified.count() > origModifiedSize || data->toDefined)) {
         data->target = d->target;
         data->path = d->path;
         if (!d->rangeIsSet) {
@@ -599,6 +650,34 @@ void QSGPathAnimation::transition(QDeclarativeStateActions &actions,
             d->pa->setEndValue(qreal(1));
             d->rangeIsSet = true;
         }
+        /*
+            NOTE: The following block relies on the fact that the previous value hasn't
+            yet been deleted, and has the same target, etc, which may be a bit fragile.
+         */
+        if (d->pa->getAnimValue()) {
+            QSGPathAnimationUpdater *prevData = static_cast<QSGPathAnimationUpdater*>(d->pa->getAnimValue());
+
+            // get the original start angle that was used (so we can exactly reverse).
+            data->startRotation = prevData->startRotation;
+
+            // treat interruptions specially, otherwise we end up with strange paths
+            if ((data->reverse || prevData->reverse) && prevData->currentV > 0 && prevData->currentV < 1) {
+                if (!data->fromDefined && !data->toDefined && !prevData->painterPath.isEmpty()) {
+                    QPointF pathPos = QDeclarativePath::sequentialPointAt(prevData->painterPath, prevData->pathLength, prevData->attributePoints, prevData->prevBez, prevData->currentV);
+                    if (!prevData->anchorPoint.isNull())
+                        pathPos -= prevData->anchorPoint;
+                    if (pathPos == data->target->pos()) {   //only treat as interruption if we interrupted ourself
+                        data->painterPath = prevData->painterPath;
+                        data->toDefined = data->fromDefined = data->fromSourced = true;
+                        data->prevBez.isValid = false;
+                        data->interruptStart = prevData->currentV;
+                        data->startRotation = prevData->startRotation;
+                        data->pathLength = prevData->pathLength;
+                        data->attributePoints = prevData->attributePoints;
+                    }
+                }
+            }
+        }
         d->pa->setFromSourcedValue(&data->fromSourced);
         d->pa->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped);
     } else {
@@ -610,7 +689,15 @@ void QSGPathAnimation::transition(QDeclarativeStateActions &actions,
 
 void QSGPathAnimationUpdater::setValue(qreal v)
 {
-    if (!fromSourced && !fromDefined) { //### check if !toDefined?
+    if (interruptStart.isValid()) {
+        if (reverse)
+            v = 1 - v;
+        qreal end = reverse ? 0.0 : 1.0;
+        v = interruptStart + v * (end-interruptStart);
+    }
+    currentV = v;
+    bool atStart = ((reverse && v == 1.0) || (!reverse && v == 0.0));
+    if (!fromSourced && (!fromDefined || !toDefined)) {
         qreal startX = reverse ? toX + anchorPoint.x() : target->x() + anchorPoint.x();
         qreal startY = reverse ? toY + anchorPoint.y() : target->y() + anchorPoint.y();
         qreal endX = reverse ? target->x() + anchorPoint.x() : toX + anchorPoint.x();
@@ -628,13 +715,13 @@ void QSGPathAnimationUpdater::setValue(qreal v)
     //adjust position according to anchor point
     if (!anchorPoint.isNull()) {
         currentPos -= anchorPoint;
-        if ((reverse && v == 1.0) || (!reverse && v == 0.0)) {
+        if (atStart) {
             if (!anchorPoint.isNull() && !fixed)
                 target->setTransformOriginPoint(anchorPoint);
         }
     }
 
-    //### too expensive to reconstruct properties each time?
+    //### could cache properties rather than reconstructing each time
     QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, "x"), currentPos.x(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
     QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, "y"), currentPos.y(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
 
@@ -644,27 +731,62 @@ void QSGPathAnimationUpdater::setValue(qreal v)
             case QSGPathAnimation::RightFirst:
                 angle = -angle;
                 break;
+            case QSGPathAnimation::TopFirst:
+                angle = -angle + 90;
+                break;
             case QSGPathAnimation::LeftFirst:
                 angle = -angle + 180;
                 break;
             case QSGPathAnimation::BottomFirst:
                 angle = -angle + 270;
                 break;
-            case QSGPathAnimation::TopFirst:
-                angle = -angle + 450;
-                break;
             default:
                 angle = 0;
                 break;
         }
+
+        if (atStart && !reverse) {
+            startRotation = target->rotation();
+
+            //shortest distance to correct orientation
+            qreal diff = angle - startRotation;
+            while (diff > 180.0) {
+                startRotation.value += 360.0;
+                diff -= 360.0;
+            }
+            while (diff < -180.0) {
+                startRotation.value -= 360.0;
+                diff += 360.0;
+            }
+        }
+
+        //smoothly transition to the desired orientation
+        if (startRotation.isValid()) {
+            if (reverse && v == 0.0)
+                angle = startRotation;
+            else if (v < entryInterval)
+                angle = angle * v / entryInterval + startRotation * (entryInterval - v) / entryInterval;
+        }
+        if (endRotation.isValid()) {
+            qreal exitStart = 1 - exitInterval;
+            if (!reverse && v == 1.0)
+                angle = endRotation;
+            else if (v > exitStart)
+                angle = endRotation * (v - exitStart) / exitInterval + angle * (exitInterval - (v - exitStart)) / exitInterval;
+        }
         QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, "rotation"), angle, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
     }
 
-    //### resetting transform causes visual jump if ending on an angle
-//    if ((reverse && v == 0.0) || (!reverse && v == 1.0)) {
-//        if (!anchorPoint.isNull() && !fixed)
-//            target->setTransformOriginPoint(QPointF());
-//    }
+    /*
+        NOTE: we don't always reset the transform origin, as it can cause a
+        visual jump if ending on an angle. This means that in some cases
+        (anchor point and orientation both specified, and ending at an angle)
+        the transform origin will always be set after running the path animation.
+     */
+    if ((reverse && v == 0.0) || (!reverse && v == 1.0)) {
+        if (!anchorPoint.isNull() && !fixed && qFuzzyIsNull(angle))
+            target->setTransformOriginPoint(QPointF());
+    }
 }
 
 QT_END_NAMESPACE
index 6406b86..af6279a 100644 (file)
@@ -136,6 +136,9 @@ class Q_AUTOTEST_EXPORT QSGPathAnimation : public QDeclarativeAbstractAnimation
     Q_PROPERTY(QSGItem *target READ target WRITE setTarget NOTIFY targetChanged)
     Q_PROPERTY(Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged)
     Q_PROPERTY(QPointF anchorPoint READ anchorPoint WRITE setAnchorPoint NOTIFY anchorPointChanged)
+    Q_PROPERTY(qreal orientationEntryInterval READ orientationEntryInterval WRITE setOrientationEntryInterval NOTIFY orientationEntryIntervalChanged)
+    Q_PROPERTY(qreal orientationExitInterval READ orientationExitInterval WRITE setOrientationExitInterval NOTIFY orientationExitIntervalChanged)
+    Q_PROPERTY(qreal endRotation READ endRotation WRITE setEndRotation NOTIFY endRotationChanged)
 
 public:
     QSGPathAnimation(QObject *parent=0);
@@ -168,6 +171,15 @@ public:
     QPointF anchorPoint() const;
     void setAnchorPoint(const QPointF &point);
 
+    qreal orientationEntryInterval() const;
+    void setOrientationEntryInterval(qreal);
+
+    qreal orientationExitInterval() const;
+    void setOrientationExitInterval(qreal);
+
+    qreal endRotation() const;
+    void setEndRotation(qreal);
+
 protected:
     virtual void transition(QDeclarativeStateActions &actions,
                             QDeclarativeProperties &modified,
@@ -181,6 +193,9 @@ Q_SIGNALS:
     void targetChanged();
     void orientationChanged(Orientation);
     void anchorPointChanged(const QPointF &);
+    void orientationEntryIntervalChanged(qreal);
+    void orientationExitIntervalChanged(qreal);
+    void endRotationChanged(qreal);
 };
 
 QT_END_NAMESPACE
index e26dbbd..276efc5 100644 (file)
@@ -96,6 +96,14 @@ public:
 class QSGPathAnimationUpdater : public QDeclarativeBulkValueUpdater
 {
 public:
+    QSGPathAnimationUpdater() : path(0), target(0), reverse(false),
+        fromSourced(false), fromDefined(false), toDefined(false),
+        toX(0), toY(0), currentV(0), orientation(QSGPathAnimation::Fixed),
+        entryInterval(0), exitInterval(0) {}
+    ~QSGPathAnimationUpdater() {}
+
+        void setValue(qreal v);
+
     QDeclarativePath *path;
 
     QPainterPath painterPath;
@@ -107,14 +115,18 @@ public:
     bool reverse;
     bool fromSourced;
     bool fromDefined;
+    bool toDefined;
     qreal toX;
     qreal toY;
+    qreal currentV;
+    QDeclarativeNullableValue<qreal> interruptStart;
+    //TODO: bundle below into common struct
     QSGPathAnimation::Orientation orientation;
     QPointF anchorPoint;
-    QSGPathAnimationUpdater() : path(0), target(0), reverse(false),
-        fromSourced(false), fromDefined(false), toX(0), toY(0), orientation(QSGPathAnimation::Fixed) {}
-    ~QSGPathAnimationUpdater() {}
-    void setValue(qreal v);
+    qreal entryInterval;
+    qreal exitInterval;
+    QDeclarativeNullableValue<qreal> endRotation;
+    QDeclarativeNullableValue<qreal> startRotation;
 };
 
 class QSGPathAnimationPrivate : public QDeclarativeAbstractAnimationPrivate
@@ -122,13 +134,16 @@ class QSGPathAnimationPrivate : public QDeclarativeAbstractAnimationPrivate
     Q_DECLARE_PUBLIC(QSGPathAnimation)
 public:
     QSGPathAnimationPrivate() : path(0), target(0),
-        rangeIsSet(false), orientation(QSGPathAnimation::Fixed), pa(0) {}
+        rangeIsSet(false), orientation(QSGPathAnimation::Fixed), entryInterval(0), exitInterval(0), pa(0) {}
 
     QDeclarativePath *path;
     QSGItem *target;
     bool rangeIsSet;
     QSGPathAnimation::Orientation orientation;
     QPointF anchorPoint;
+    qreal entryInterval;
+    qreal exitInterval;
+    QDeclarativeNullableValue<qreal> endRotation;
     QDeclarativeBulkValueAnimator *pa;
 };
 
index 104ebde..17caa9a 100644 (file)
@@ -156,6 +156,10 @@ public:
         animValue = value;
         policy = p;
     }
+    QDeclarativeBulkValueUpdater *getAnimValue() const
+    {
+        return animValue;
+    }
     void setFromSourcedValue(bool *value)
     {
         fromSourced = value;
index 57387f5..f923413 100644 (file)
@@ -149,6 +149,20 @@ bool QDeclarativePath::isClosed() const
     return d->closed;
 }
 
+bool QDeclarativePath::hasEnd() const
+{
+    Q_D(const QDeclarativePath);
+    for (int i = d->_pathElements.count() - 1; i > -1; --i) {
+        if (QDeclarativeCurve *curve = qobject_cast<QDeclarativeCurve *>(d->_pathElements.at(i))) {
+            if ((!curve->hasX() && !curve->hasRelativeX()) || (!curve->hasY() && !curve->hasRelativeY()))
+                return false;
+            else
+                return true;
+        }
+    }
+    return hasStartX() && hasStartY();
+}
+
 /*!
     \qmlproperty list<PathElement> QtQuick2::Path::pathElements
     This property holds the objects composing the path.
@@ -400,10 +414,11 @@ QStringList QDeclarativePath::attributes() const
     return d->_attributes;
 }
 
-static inline QBezier nextBezier(const QPainterPath &path, int *from, qreal *bezLength, bool reverse = false)
+static inline QBezier nextBezier(const QPainterPath &path, int *current, qreal *bezLength, bool reverse = false)
 {
     const int lastElement = reverse ? 0 : path.elementCount() - 1;
-    for (int i=*from; reverse ? i >= lastElement : i <= lastElement; reverse ? --i : ++i) {
+    const int start = reverse ? *current - 1 : *current + 1;
+    for (int i=start; reverse ? i >= lastElement : i <= lastElement; reverse ? --i : ++i) {
         const QPainterPath::Element &e = path.elementAt(i);
 
         switch (e.type) {
@@ -415,7 +430,7 @@ static inline QBezier nextBezier(const QPainterPath &path, int *from, qreal *bez
             *bezLength = line.length();
             QPointF a = path.elementAt(i-1);
             QPointF delta = e - a;
-            *from = reverse ? i-1 : i+1;
+            *current = i;
             return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
         }
         case QPainterPath::CurveToElement:
@@ -425,14 +440,14 @@ static inline QBezier nextBezier(const QPainterPath &path, int *from, qreal *bez
                                             path.elementAt(i+1),
                                             path.elementAt(i+2));
             *bezLength = b.length();
-            *from = reverse ? i-1 : i+3;
+            *current = i;
             return b;
         }
         default:
             break;
         }
     }
-    *from = lastElement;
+    *current = lastElement;
     *bezLength = 0;
     return QBezier();
 }
@@ -455,7 +470,7 @@ void QDeclarativePath::createPointCache() const
     const int lastElement = d->_path.elementCount() - 1;
     d->_pointCache.resize(points+1);
 
-    int currElement = 0;
+    int currElement = -1;
     qreal bezLength = 0;
     QBezier currBez = nextBezier(d->_path, &currElement, &bezLength);
     qreal currLength = bezLength;
@@ -518,7 +533,7 @@ QPointF QDeclarativePath::forwardsPointAt(const QPainterPath &path, const qreal
 
     const int lastElement = path.elementCount() - 1;
     bool haveCachedBez = prevBez.isValid;
-    int currElement = haveCachedBez ? prevBez.element : 0;
+    int currElement = haveCachedBez ? prevBez.element : -1;
     qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
     QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength);
     qreal currLength = haveCachedBez ? prevBez.currLength : bezLength;
@@ -536,14 +551,8 @@ QPointF QDeclarativePath::forwardsPointAt(const QPainterPath &path, const qreal
             qreal spc = prevOrigPercent + elementPercent * point.scale;
 
             while (spc > epc) {
-                if (currElement > lastElement)
-                    break;
+                Q_ASSERT(!(currElement > lastElement));
                 currBez = nextBezier(path, &currElement, &bezLength);
-                /*if (bezLength == 0.0) {
-                    currLength = pathLength;
-                    epc = 1.0;
-                    break;
-                }*/
                 currLength += bezLength;
                 epc = currLength / pathLength;
             }
@@ -579,7 +588,7 @@ QPointF QDeclarativePath::backwardsPointAt(const QPainterPath &path, const qreal
 
     const int firstElement = 0;
     bool haveCachedBez = prevBez.isValid;
-    int currElement = haveCachedBez ? prevBez.element : path.elementCount() - 1;
+    int currElement = haveCachedBez ? prevBez.element : path.elementCount();
     qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
     QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength, true /*reverse*/);
     qreal currLength = haveCachedBez ? prevBez.currLength : pathLength;
@@ -596,14 +605,8 @@ QPointF QDeclarativePath::backwardsPointAt(const QPainterPath &path, const qreal
             qreal spc = prevPoint.origpercent + elementPercent * point.scale;
 
             while (spc < epc) {
-                if (currElement < firstElement)
-                    break;
+                Q_ASSERT(!(currElement < firstElement));
                 currBez = nextBezier(path, &currElement, &bezLength, true /*reverse*/);
-                /*if (bezLength == 0.0) {
-                    currLength = 0;
-                    epc = 0.0;
-                    break;
-                }*/
                 currLength = prevLength;
                 epc = (currLength - bezLength) / pathLength;
             }
index 4ce1bcf..e997240 100644 (file)
@@ -380,6 +380,7 @@ public:
     bool hasStartY() const;
 
     bool isClosed() const;
+    bool hasEnd() const;
 
     QPainterPath path() const;
     QStringList attributes() const;
diff --git a/tests/auto/declarative/qdeclarativeanimations/data/pathAnimation2.qml b/tests/auto/declarative/qdeclarativeanimations/data/pathAnimation2.qml
new file mode 100644 (file)
index 0000000..951c5b2
--- /dev/null
@@ -0,0 +1,26 @@
+import QtQuick 2.0
+
+Rectangle {
+    width: 400
+    height: 400
+
+    Rectangle {
+        id: redRect
+        color: "red"
+        width: 100; height: 100
+        x: 50; y: 50
+    }
+
+    PathAnimation {
+        target: redRect
+        duration: 100;
+        endRotation: 0
+        orientationEntryInterval: .1
+        orientationExitInterval: .1
+        orientation: PathAnimation.RightFirst
+        path: Path {
+            startX: 50; startY: 50
+            PathLine { x: 300; y: 300 }
+        }
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeanimations/data/pathAnimationNoStart.qml b/tests/auto/declarative/qdeclarativeanimations/data/pathAnimationNoStart.qml
new file mode 100644 (file)
index 0000000..be3501f
--- /dev/null
@@ -0,0 +1,27 @@
+import QtQuick 2.0
+
+Rectangle {
+    width: 400
+    height: 400
+
+    Rectangle {
+        id: redRect
+        color: "red"
+        width: 100; height: 100
+        x: 50; y: 50
+    }
+
+    PathAnimation {
+        target: redRect
+        duration: 100;
+        path: Path {
+            //no startX/Y defined (should automatically start from redRects pos)
+            PathCubic {
+                x: 300; y: 300
+
+                control1X: 300; control1Y: 50
+                control2X: 50; control2Y: 300
+            }
+        }
+    }
+}
index df840e9..303268e 100644 (file)
@@ -50,6 +50,9 @@
 #include <QVariantAnimation>
 #include <QEasingCurve>
 
+#include <limits.h>
+#include <math.h>
+
 #include "../../../shared/util.h"
 
 #ifdef Q_OS_SYMBIAN
@@ -72,6 +75,7 @@ private slots:
     void simpleRotation();
     void simplePath();
     void pathInterpolator();
+    void pathWithNoStart();
     void alwaysRunToEnd();
     void complete();
     void resume();
@@ -220,33 +224,71 @@ void tst_qdeclarativeanimations::simpleRotation()
 
 void tst_qdeclarativeanimations::simplePath()
 {
-    QDeclarativeEngine engine;
-    QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/pathAnimation.qml"));
-    QSGRectangle *rect = qobject_cast<QSGRectangle*>(c.create());
-    QVERIFY(rect);
+    {
+        QDeclarativeEngine engine;
+        QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/pathAnimation.qml"));
+        QSGRectangle *rect = qobject_cast<QSGRectangle*>(c.create());
+        QVERIFY(rect);
 
-    QSGRectangle *redRect = rect->findChild<QSGRectangle*>();
-    QVERIFY(redRect);
-    QSGPathAnimation *pathAnim = rect->findChild<QSGPathAnimation*>();
-    QVERIFY(pathAnim);
+        QSGRectangle *redRect = rect->findChild<QSGRectangle*>();
+        QVERIFY(redRect);
+        QSGPathAnimation *pathAnim = rect->findChild<QSGPathAnimation*>();
+        QVERIFY(pathAnim);
+
+        pathAnim->start();
+        pathAnim->pause();
+
+        pathAnim->setCurrentTime(30);
+        QCOMPARE(redRect->x(), qreal(167));
+        QCOMPARE(redRect->y(), qreal(104));
+
+        pathAnim->setCurrentTime(100);
+        QCOMPARE(redRect->x(), qreal(300));
+        QCOMPARE(redRect->y(), qreal(300));
+
+        //verify animation runs to end
+        pathAnim->start();
+        QCOMPARE(redRect->x(), qreal(50));
+        QCOMPARE(redRect->y(), qreal(50));
+        QTRY_COMPARE(redRect->x(), qreal(300));
+        QCOMPARE(redRect->y(), qreal(300));
+
+        pathAnim->setOrientation(QSGPathAnimation::RightFirst);
+        QCOMPARE(pathAnim->orientation(), QSGPathAnimation::RightFirst);
+        pathAnim->start();
+        QTRY_VERIFY(redRect->rotation() != 0);
+        pathAnim->stop();
+    }
 
-    pathAnim->start();
-    pathAnim->pause();
+    {
+        QDeclarativeEngine engine;
+        QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/pathAnimation2.qml"));
+        QSGRectangle *rect = qobject_cast<QSGRectangle*>(c.create());
+        QVERIFY(rect);
 
-    pathAnim->setCurrentTime(50);
-    QCOMPARE(redRect->x(), qreal(175));
-    QCOMPARE(redRect->y(), qreal(175));
+        QSGRectangle *redRect = rect->findChild<QSGRectangle*>();
+        QVERIFY(redRect);
+        QSGPathAnimation *pathAnim = rect->findChild<QSGPathAnimation*>();
+        QVERIFY(pathAnim);
 
-    pathAnim->setCurrentTime(100);
-    QCOMPARE(redRect->x(), qreal(300));
-    QCOMPARE(redRect->y(), qreal(300));
+        QCOMPARE(pathAnim->orientation(), QSGPathAnimation::RightFirst);
 
-    //verify animation runs to end
-    pathAnim->start();
-    QCOMPARE(redRect->x(), qreal(50));
-    QCOMPARE(redRect->y(), qreal(50));
-    QTRY_COMPARE(redRect->x(), qreal(300));
-    QCOMPARE(redRect->y(), qreal(300));
+        pathAnim->start();
+        pathAnim->pause();
+        QCOMPARE(redRect->x(), qreal(50));
+        QCOMPARE(redRect->y(), qreal(50));
+        QCOMPARE(redRect->rotation(), qreal(-360));
+
+        pathAnim->setCurrentTime(50);
+        QCOMPARE(redRect->x(), qreal(175));
+        QCOMPARE(redRect->y(), qreal(175));
+        QCOMPARE(redRect->rotation(), qreal(-315));
+
+        pathAnim->setCurrentTime(100);
+        QCOMPARE(redRect->x(), qreal(300));
+        QCOMPARE(redRect->y(), qreal(300));
+        QCOMPARE(redRect->rotation(), qreal(0));
+    }
 }
 
 void tst_qdeclarativeanimations::pathInterpolator()
@@ -274,6 +316,40 @@ void tst_qdeclarativeanimations::pathInterpolator()
     QCOMPARE(interpolator->angle(), qreal(0));
 }
 
+void tst_qdeclarativeanimations::pathWithNoStart()
+{
+    QDeclarativeEngine engine;
+    QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/pathAnimationNoStart.qml"));
+    QSGRectangle *rect = qobject_cast<QSGRectangle*>(c.create());
+    QVERIFY(rect);
+
+    QSGRectangle *redRect = rect->findChild<QSGRectangle*>();
+    QVERIFY(redRect);
+    QSGPathAnimation *pathAnim = rect->findChild<QSGPathAnimation*>();
+    QVERIFY(pathAnim);
+
+    pathAnim->start();
+    pathAnim->pause();
+    QCOMPARE(redRect->x(), qreal(50));
+    QCOMPARE(redRect->y(), qreal(50));
+
+    pathAnim->setCurrentTime(50);
+    QCOMPARE(redRect->x(), qreal(175));
+    QCOMPARE(redRect->y(), qreal(175));
+
+    pathAnim->setCurrentTime(100);
+    QCOMPARE(redRect->x(), qreal(300));
+    QCOMPARE(redRect->y(), qreal(300));
+
+    redRect->setX(100);
+    redRect->setY(100);
+    pathAnim->start();
+    QCOMPARE(redRect->x(), qreal(100));
+    QCOMPARE(redRect->y(), qreal(100));
+    QTRY_COMPARE(redRect->x(), qreal(300));
+    QCOMPARE(redRect->y(), qreal(300));
+}
+
 void tst_qdeclarativeanimations::alwaysRunToEnd()
 {
     QSGRectangle rect;