Add new path functionality.
authorMichael Brasser <michael.brasser@nokia.com>
Wed, 31 Aug 2011 22:47:56 +0000 (08:47 +1000)
committerQt by Nokia <qt-info@nokia.com>
Wed, 7 Sep 2011 22:05:26 +0000 (00:05 +0200)
Introduces new elements PathCurve, PathArc, PathSvg,
PathAnimation, and PathInterpolator.

Change-Id: I0f5b6284511f332e826ed373018418d2e2a8c07e
Reviewed-on: http://codereview.qt-project.org/4220
Reviewed-by: Martin Jones <martin.jones@nokia.com>

26 files changed:
src/declarative/items/context2d/qsgcontext2d.cpp
src/declarative/items/qsganimation.cpp
src/declarative/items/qsganimation_p.h
src/declarative/items/qsganimation_p_p.h
src/declarative/items/qsgcanvas.cpp
src/declarative/items/qsgitem.cpp
src/declarative/items/qsgitem.h
src/declarative/items/qsgitem_p.h
src/declarative/items/qsgitemsmodule.cpp
src/declarative/util/qdeclarativepath.cpp
src/declarative/util/qdeclarativepath_p.h
src/declarative/util/qdeclarativepath_p_p.h
src/declarative/util/qdeclarativepathinterpolator.cpp [new file with mode: 0644]
src/declarative/util/qdeclarativepathinterpolator_p.h [new file with mode: 0644]
src/declarative/util/qdeclarativesvgparser.cpp [new file with mode: 0644]
src/declarative/util/qdeclarativesvgparser_p.h [new file with mode: 0644]
src/declarative/util/util.pri
tests/auto/declarative/qdeclarativeanimations/data/pathAnimation.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeanimations/data/pathInterpolator.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeanimations/data/pathTransition.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeanimations/tst_qdeclarativeanimations.cpp
tests/auto/declarative/qdeclarativepath/data/arc.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativepath/data/curve.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativepath/data/svg.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativepath/qdeclarativepath.pro [new file with mode: 0644]
tests/auto/declarative/qdeclarativepath/tst_qdeclarativepath.cpp [new file with mode: 0644]

index 4cfe1ba..a2a57cb 100644 (file)
@@ -46,6 +46,7 @@
 #include <QtOpenGL/qglframebufferobject.h>
 #include <QtCore/qdebug.h>
 #include "private/qsgcontext_p.h"
+#include "private/qdeclarativesvgparser_p.h"
 
 #include <QtGui/qgraphicsitem.h>
 #include <QtGui/qapplication.h>
@@ -82,7 +83,6 @@ void copy_vector(QVector<T>* dst, const QVector<T>& src)
 // But it really should be considered private API
 void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0);
 void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0);
-static bool parsePathDataFast(const QString &dataStr, QPainterPath &path);
 #define DEGREES(t) ((t) * 180.0 / Q_PI)
 #define qClamp(val, min, max) qMin(qMax(val, min), max)
 
@@ -2312,7 +2312,7 @@ void QSGContext2D::setPath(QSGCanvasPath* path)
 QSGCanvasPath* QSGContext2D::createPath(const QString& pathString)
 {
     QPainterPath path;
-    if (parsePathDataFast(pathString, path)) {
+    if (QDeclarativeSvgParser::parsePathDataFast(pathString, path)) {
         return new QSGCanvasPath(path, this);
     }
     return 0;
@@ -2573,592 +2573,11 @@ bool QSGContext2D::isPointInPath(qreal x, qreal y) const
     return d->path.contains(QPointF(x, y));
 }
 
-//copied from QtSvg (qsvghandler.cpp).
-Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok);
-// '0' is 0x30 and '9' is 0x39
-static inline bool isDigit(ushort ch)
-{
-    static quint16 magic = 0x3ff;
-    return ((ch >> 4) == 3) && (magic >> (ch & 15));
-}
-
-static qreal toDouble(const QChar *&str)
-{
-    const int maxLen = 255;//technically doubles can go til 308+ but whatever
-    char temp[maxLen+1];
-    int pos = 0;
-
-    if (*str == QLatin1Char('-')) {
-        temp[pos++] = '-';
-        ++str;
-    } else if (*str == QLatin1Char('+')) {
-        ++str;
-    }
-    while (isDigit(str->unicode()) && pos < maxLen) {
-        temp[pos++] = str->toLatin1();
-        ++str;
-    }
-    if (*str == QLatin1Char('.') && pos < maxLen) {
-        temp[pos++] = '.';
-        ++str;
-    }
-    while (isDigit(str->unicode()) && pos < maxLen) {
-        temp[pos++] = str->toLatin1();
-        ++str;
-    }
-    bool exponent = false;
-    if ((*str == QLatin1Char('e') || *str == QLatin1Char('E')) && pos < maxLen) {
-        exponent = true;
-        temp[pos++] = 'e';
-        ++str;
-        if ((*str == QLatin1Char('-') || *str == QLatin1Char('+')) && pos < maxLen) {
-            temp[pos++] = str->toLatin1();
-            ++str;
-        }
-        while (isDigit(str->unicode()) && pos < maxLen) {
-            temp[pos++] = str->toLatin1();
-            ++str;
-        }
-    }
-
-    temp[pos] = '\0';
-
-    qreal val;
-    if (!exponent && pos < 10) {
-        int ival = 0;
-        const char *t = temp;
-        bool neg = false;
-        if(*t == '-') {
-            neg = true;
-            ++t;
-        }
-        while(*t && *t != '.') {
-            ival *= 10;
-            ival += (*t) - '0';
-            ++t;
-        }
-        if(*t == '.') {
-            ++t;
-            int div = 1;
-            while(*t) {
-                ival *= 10;
-                ival += (*t) - '0';
-                div *= 10;
-                ++t;
-            }
-            val = ((qreal)ival)/((qreal)div);
-        } else {
-            val = ival;
-        }
-        if (neg)
-            val = -val;
-    } else {
-#if defined(Q_WS_QWS) && !defined(Q_OS_VXWORKS)
-        if(sizeof(qreal) == sizeof(float))
-            val = strtof(temp, 0);
-        else
-#endif
-        {
-            bool ok = false;
-            val = qstrtod(temp, 0, &ok);
-        }
-    }
-    return val;
-
-}
-static qreal toDouble(const QString &str, bool *ok = NULL)
-{
-    const QChar *c = str.constData();
-    qreal res = toDouble(c);
-    if (ok) {
-        *ok = ((*c) == QLatin1Char('\0'));
-    }
-    return res;
-}
-
-static qreal toDouble(const QStringRef &str, bool *ok = NULL)
-{
-    const QChar *c = str.constData();
-    qreal res = toDouble(c);
-    if (ok) {
-        *ok = (c == (str.constData() + str.length()));
-    }
-    return res;
-}
-static inline void parseNumbersArray(const QChar *&str, QVarLengthArray<qreal, 8> &points)
-{
-    while (str->isSpace())
-        ++str;
-    while (isDigit(str->unicode()) ||
-           *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
-           *str == QLatin1Char('.')) {
-
-        points.append(toDouble(str));
-
-        while (str->isSpace())
-            ++str;
-        if (*str == QLatin1Char(','))
-            ++str;
-
-        //eat the rest of space
-        while (str->isSpace())
-            ++str;
-    }
-}
-
-static void pathArcSegment(QPainterPath &path,
-                           qreal xc, qreal yc,
-                           qreal th0, qreal th1,
-                           qreal rx, qreal ry, qreal xAxisRotation)
-{
-    qreal sinTh, cosTh;
-    qreal a00, a01, a10, a11;
-    qreal x1, y1, x2, y2, x3, y3;
-    qreal t;
-    qreal thHalf;
-
-    sinTh = qSin(xAxisRotation * (Q_PI / 180.0));
-    cosTh = qCos(xAxisRotation * (Q_PI / 180.0));
-
-    a00 =  cosTh * rx;
-    a01 = -sinTh * ry;
-    a10 =  sinTh * rx;
-    a11 =  cosTh * ry;
-
-    thHalf = 0.5 * (th1 - th0);
-    t = (8.0 / 3.0) * qSin(thHalf * 0.5) * qSin(thHalf * 0.5) / qSin(thHalf);
-    x1 = xc + qCos(th0) - t * qSin(th0);
-    y1 = yc + qSin(th0) + t * qCos(th0);
-    x3 = xc + qCos(th1);
-    y3 = yc + qSin(th1);
-    x2 = x3 + t * qSin(th1);
-    y2 = y3 - t * qCos(th1);
-
-    path.cubicTo(a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
-                 a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
-                 a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
-}
-
-static void pathArc(QPainterPath &path,
-                    qreal               rx,
-                    qreal               ry,
-                    qreal               x_axis_rotation,
-                    int         large_arc_flag,
-                    int         sweep_flag,
-                    qreal               x,
-                    qreal               y,
-                    qreal curx, qreal cury)
-{
-    qreal sin_th, cos_th;
-    qreal a00, a01, a10, a11;
-    qreal x0, y0, x1, y1, xc, yc;
-    qreal d, sfactor, sfactor_sq;
-    qreal th0, th1, th_arc;
-    int i, n_segs;
-    qreal dx, dy, dx1, dy1, Pr1, Pr2, Px, Py, check;
-
-    rx = qAbs(rx);
-    ry = qAbs(ry);
-
-    sin_th = qSin(x_axis_rotation * (Q_PI / 180.0));
-    cos_th = qCos(x_axis_rotation * (Q_PI / 180.0));
-
-    dx = (curx - x) / 2.0;
-    dy = (cury - y) / 2.0;
-    dx1 =  cos_th * dx + sin_th * dy;
-    dy1 = -sin_th * dx + cos_th * dy;
-    Pr1 = rx * rx;
-    Pr2 = ry * ry;
-    Px = dx1 * dx1;
-    Py = dy1 * dy1;
-    /* Spec : check if radii are large enough */
-    check = Px / Pr1 + Py / Pr2;
-    if (check > 1) {
-        rx = rx * qSqrt(check);
-        ry = ry * qSqrt(check);
-    }
-
-    a00 =  cos_th / rx;
-    a01 =  sin_th / rx;
-    a10 = -sin_th / ry;
-    a11 =  cos_th / ry;
-    x0 = a00 * curx + a01 * cury;
-    y0 = a10 * curx + a11 * cury;
-    x1 = a00 * x + a01 * y;
-    y1 = a10 * x + a11 * y;
-    /* (x0, y0) is current point in transformed coordinate space.
-       (x1, y1) is new point in transformed coordinate space.
-
-       The arc fits a unit-radius circle in this space.
-    */
-    d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
-    sfactor_sq = 1.0 / d - 0.25;
-    if (sfactor_sq < 0) sfactor_sq = 0;
-    sfactor = qSqrt(sfactor_sq);
-    if (sweep_flag == large_arc_flag) sfactor = -sfactor;
-    xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
-    yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
-    /* (xc, yc) is center of the circle. */
-
-    th0 = qAtan2(y0 - yc, x0 - xc);
-    th1 = qAtan2(y1 - yc, x1 - xc);
-
-    th_arc = th1 - th0;
-    if (th_arc < 0 && sweep_flag)
-        th_arc += 2 * Q_PI;
-    else if (th_arc > 0 && !sweep_flag)
-        th_arc -= 2 * Q_PI;
-
-    n_segs = qCeil(qAbs(th_arc / (Q_PI * 0.5 + 0.001)));
-
-    for (i = 0; i < n_segs; i++) {
-        pathArcSegment(path, xc, yc,
-                       th0 + i * th_arc / n_segs,
-                       th0 + (i + 1) * th_arc / n_segs,
-                       rx, ry, x_axis_rotation);
-    }
-}
-
-
-static bool parsePathDataFast(const QString &dataStr, QPainterPath &path)
-{
-    qreal x0 = 0, y0 = 0;              // starting point
-    qreal x = 0, y = 0;                // current point
-    char lastMode = 0;
-    QPointF ctrlPt;
-    const QChar *str = dataStr.constData();
-    const QChar *end = str + dataStr.size();
-
-    while (str != end) {
-        while (str->isSpace())
-            ++str;
-        QChar pathElem = *str;
-        ++str;
-        QChar endc = *end;
-        *const_cast<QChar *>(end) = 0; // parseNumbersArray requires 0-termination that QStringRef cannot guarantee
-        QVarLengthArray<qreal, 8> arg;
-        parseNumbersArray(str, arg);
-        *const_cast<QChar *>(end) = endc;
-        if (pathElem == QLatin1Char('z') || pathElem == QLatin1Char('Z'))
-            arg.append(0);//dummy
-        const qreal *num = arg.constData();
-        int count = arg.count();
-        while (count > 0) {
-            qreal offsetX = x;        // correction offsets
-            qreal offsetY = y;        // for relative commands
-            switch (pathElem.unicode()) {
-            case 'm': {
-                if (count < 2) {
-                    num++;
-                    count--;
-                    break;
-                }
-                x = x0 = num[0] + offsetX;
-                y = y0 = num[1] + offsetY;
-                num += 2;
-                count -= 2;
-                path.moveTo(x0, y0);
-
-                 // As per 1.2  spec 8.3.2 The "moveto" commands
-                 // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
-                 // the subsequent pairs shall be treated as implicit 'lineto' commands.
-                 pathElem = QLatin1Char('l');
-            }
-                break;
-            case 'M': {
-                if (count < 2) {
-                    num++;
-                    count--;
-                    break;
-                }
-                x = x0 = num[0];
-                y = y0 = num[1];
-                num += 2;
-                count -= 2;
-                path.moveTo(x0, y0);
-
-                // As per 1.2  spec 8.3.2 The "moveto" commands
-                // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
-                // the subsequent pairs shall be treated as implicit 'lineto' commands.
-                pathElem = QLatin1Char('L');
-            }
-                break;
-            case 'z':
-            case 'Z': {
-                x = x0;
-                y = y0;
-                count--; // skip dummy
-                num++;
-                path.closeSubpath();
-            }
-                break;
-            case 'l': {
-                if (count < 2) {
-                    num++;
-                    count--;
-                    break;
-                }
-                x = num[0] + offsetX;
-                y = num[1] + offsetY;
-                num += 2;
-                count -= 2;
-                path.lineTo(x, y);
-
-            }
-                break;
-            case 'L': {
-                if (count < 2) {
-                    num++;
-                    count--;
-                    break;
-                }
-                x = num[0];
-                y = num[1];
-                num += 2;
-                count -= 2;
-                path.lineTo(x, y);
-            }
-                break;
-            case 'h': {
-                x = num[0] + offsetX;
-                num++;
-                count--;
-                path.lineTo(x, y);
-            }
-                break;
-            case 'H': {
-                x = num[0];
-                num++;
-                count--;
-                path.lineTo(x, y);
-            }
-                break;
-            case 'v': {
-                y = num[0] + offsetY;
-                num++;
-                count--;
-                path.lineTo(x, y);
-            }
-                break;
-            case 'V': {
-                y = num[0];
-                num++;
-                count--;
-                path.lineTo(x, y);
-            }
-                break;
-            case 'c': {
-                if (count < 6) {
-                    num += count;
-                    count = 0;
-                    break;
-                }
-                QPointF c1(num[0] + offsetX, num[1] + offsetY);
-                QPointF c2(num[2] + offsetX, num[3] + offsetY);
-                QPointF e(num[4] + offsetX, num[5] + offsetY);
-                num += 6;
-                count -= 6;
-                path.cubicTo(c1, c2, e);
-                ctrlPt = c2;
-                x = e.x();
-                y = e.y();
-                break;
-            }
-            case 'C': {
-                if (count < 6) {
-                    num += count;
-                    count = 0;
-                    break;
-                }
-                QPointF c1(num[0], num[1]);
-                QPointF c2(num[2], num[3]);
-                QPointF e(num[4], num[5]);
-                num += 6;
-                count -= 6;
-                path.cubicTo(c1, c2, e);
-                ctrlPt = c2;
-                x = e.x();
-                y = e.y();
-                break;
-            }
-            case 's': {
-                if (count < 4) {
-                    num += count;
-                    count = 0;
-                    break;
-                }
-                QPointF c1;
-                if (lastMode == 'c' || lastMode == 'C' ||
-                    lastMode == 's' || lastMode == 'S')
-                    c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
-                else
-                    c1 = QPointF(x, y);
-                QPointF c2(num[0] + offsetX, num[1] + offsetY);
-                QPointF e(num[2] + offsetX, num[3] + offsetY);
-                num += 4;
-                count -= 4;
-                path.cubicTo(c1, c2, e);
-                ctrlPt = c2;
-                x = e.x();
-                y = e.y();
-                break;
-            }
-            case 'S': {
-                if (count < 4) {
-                    num += count;
-                    count = 0;
-                    break;
-                }
-                QPointF c1;
-                if (lastMode == 'c' || lastMode == 'C' ||
-                    lastMode == 's' || lastMode == 'S')
-                    c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
-                else
-                    c1 = QPointF(x, y);
-                QPointF c2(num[0], num[1]);
-                QPointF e(num[2], num[3]);
-                num += 4;
-                count -= 4;
-                path.cubicTo(c1, c2, e);
-                ctrlPt = c2;
-                x = e.x();
-                y = e.y();
-                break;
-            }
-            case 'q': {
-                if (count < 4) {
-                    num += count;
-                    count = 0;
-                    break;
-                }
-                QPointF c(num[0] + offsetX, num[1] + offsetY);
-                QPointF e(num[2] + offsetX, num[3] + offsetY);
-                num += 4;
-                count -= 4;
-                path.quadTo(c, e);
-                ctrlPt = c;
-                x = e.x();
-                y = e.y();
-                break;
-            }
-            case 'Q': {
-                if (count < 4) {
-                    num += count;
-                    count = 0;
-                    break;
-                }
-                QPointF c(num[0], num[1]);
-                QPointF e(num[2], num[3]);
-                num += 4;
-                count -= 4;
-                path.quadTo(c, e);
-                ctrlPt = c;
-                x = e.x();
-                y = e.y();
-                break;
-            }
-            case 't': {
-                if (count < 2) {
-                    num += count;
-                    count = 0;
-                    break;
-                }
-                QPointF e(num[0] + offsetX, num[1] + offsetY);
-                num += 2;
-                count -= 2;
-                QPointF c;
-                if (lastMode == 'q' || lastMode == 'Q' ||
-                    lastMode == 't' || lastMode == 'T')
-                    c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
-                else
-                    c = QPointF(x, y);
-                path.quadTo(c, e);
-                ctrlPt = c;
-                x = e.x();
-                y = e.y();
-                break;
-            }
-            case 'T': {
-                if (count < 2) {
-                    num += count;
-                    count = 0;
-                    break;
-                }
-                QPointF e(num[0], num[1]);
-                num += 2;
-                count -= 2;
-                QPointF c;
-                if (lastMode == 'q' || lastMode == 'Q' ||
-                    lastMode == 't' || lastMode == 'T')
-                    c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
-                else
-                    c = QPointF(x, y);
-                path.quadTo(c, e);
-                ctrlPt = c;
-                x = e.x();
-                y = e.y();
-                break;
-            }
-            case 'a': {
-                if (count < 7) {
-                    num += count;
-                    count = 0;
-                    break;
-                }
-                qreal rx = (*num++);
-                qreal ry = (*num++);
-                qreal xAxisRotation = (*num++);
-                qreal largeArcFlag  = (*num++);
-                qreal sweepFlag = (*num++);
-                qreal ex = (*num++) + offsetX;
-                qreal ey = (*num++) + offsetY;
-                count -= 7;
-                qreal curx = x;
-                qreal cury = y;
-                pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
-                        int(sweepFlag), ex, ey, curx, cury);
-
-                x = ex;
-                y = ey;
-            }
-                break;
-            case 'A': {
-                if (count < 7) {
-                    num += count;
-                    count = 0;
-                    break;
-                }
-                qreal rx = (*num++);
-                qreal ry = (*num++);
-                qreal xAxisRotation = (*num++);
-                qreal largeArcFlag  = (*num++);
-                qreal sweepFlag = (*num++);
-                qreal ex = (*num++);
-                qreal ey = (*num++);
-                count -= 7;
-                qreal curx = x;
-                qreal cury = y;
-                pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
-                        int(sweepFlag), ex, ey, curx, cury);
-
-                x = ex;
-                y = ey;
-            }
-                break;
-            default:
-                return false;
-            }
-            lastMode = pathElem.toLatin1();
-        }
-    }
-    return true;
-}
-
 void QSGContext2D::setPathString(const QString& path)
 {
     Q_D(QSGContext2D);
     d->path = QPainterPath();
-    parsePathDataFast(path, d->path);
+    QDeclarativeSvgParser::parsePathDataFast(path, d->path);
 }
 
 QList<int> QSGContext2D::getImageData(qreal sx, qreal sy, qreal sw, qreal sh)
index 446dc5a..6fadbbf 100644 (file)
@@ -43,6 +43,9 @@
 #include "qsganimation_p_p.h"
 #include "qsgstateoperations_p.h"
 
+#include <qdeclarativeproperty_p.h>
+#include <qdeclarativepath_p.h>
+
 #include <QtDeclarative/qdeclarativeinfo.h>
 #include <QtCore/qmath.h>
 #include <QtCore/qsequentialanimationgroup.h>
@@ -438,4 +441,229 @@ void QSGAnchorAnimation::transition(QDeclarativeStateActions &actions,
     }
 }
 
+QSGPathAnimation::QSGPathAnimation(QObject *parent)
+: QDeclarativeAbstractAnimation(*(new QSGPathAnimationPrivate), parent)
+{
+    Q_D(QSGPathAnimation);
+    d->pa = new QDeclarativeBulkValueAnimator;
+    QDeclarative_setParent_noEvent(d->pa, this);
+}
+
+QSGPathAnimation::~QSGPathAnimation()
+{
+}
+
+int QSGPathAnimation::duration() const
+{
+    Q_D(const QSGPathAnimation);
+    return d->pa->duration();
+}
+
+void QSGPathAnimation::setDuration(int duration)
+{
+    if (duration < 0) {
+        qmlInfo(this) << tr("Cannot set a duration of < 0");
+        return;
+    }
+
+    Q_D(QSGPathAnimation);
+    if (d->pa->duration() == duration)
+        return;
+    d->pa->setDuration(duration);
+    emit durationChanged(duration);
+}
+
+QEasingCurve QSGPathAnimation::easing() const
+{
+    Q_D(const QSGPathAnimation);
+    return d->pa->easingCurve();
+}
+
+void QSGPathAnimation::setEasing(const QEasingCurve &e)
+{
+    Q_D(QSGPathAnimation);
+    if (d->pa->easingCurve() == e)
+        return;
+
+    d->pa->setEasingCurve(e);
+    emit easingChanged(e);
+}
+
+QDeclarativePath *QSGPathAnimation::path() const
+{
+    Q_D(const QSGPathAnimation);
+    return d->path;
+}
+
+void QSGPathAnimation::setPath(QDeclarativePath *path)
+{
+    Q_D(QSGPathAnimation);
+    if (d->path == path)
+        return;
+
+    d->path = path;
+    emit pathChanged();
+}
+
+QSGItem *QSGPathAnimation::target() const
+{
+    Q_D(const QSGPathAnimation);
+    return d->target;
+}
+
+void QSGPathAnimation::setTarget(QSGItem *target)
+{
+    Q_D(QSGPathAnimation);
+    if (d->target == target)
+        return;
+
+    d->target = target;
+    emit targetChanged();
+}
+
+QSGPathAnimation::Orientation QSGPathAnimation::orientation() const
+{
+    Q_D(const QSGPathAnimation);
+    return d->orientation;
+}
+
+void QSGPathAnimation::setOrientation(Orientation orientation)
+{
+    Q_D(QSGPathAnimation);
+    if (d->orientation == orientation)
+        return;
+
+    d->orientation = orientation;
+    emit orientationChanged(d->orientation);
+}
+
+QPointF QSGPathAnimation::anchorPoint() const
+{
+    Q_D(const QSGPathAnimation);
+    return d->anchorPoint;
+}
+
+void QSGPathAnimation::setAnchorPoint(const QPointF &point)
+{
+    Q_D(QSGPathAnimation);
+    if (d->anchorPoint == point)
+        return;
+
+    d->anchorPoint = point;
+    emit anchorPointChanged(point);
+}
+
+QAbstractAnimation *QSGPathAnimation::qtAnimation()
+{
+    Q_D(QSGPathAnimation);
+    return d->pa;
+}
+
+void QSGPathAnimation::transition(QDeclarativeStateActions &actions,
+                                           QDeclarativeProperties &modified,
+                                           TransitionDirection direction)
+{
+    Q_D(QSGPathAnimation);
+    QSGPathAnimationUpdater *data = new QSGPathAnimationUpdater;
+
+    data->orientation = d->orientation;
+    data->anchorPoint = d->anchorPoint;
+    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?
+    int origModifiedSize = modified.count();
+
+    for (int i = 0; i < actions.count(); ++i) {
+        QDeclarativeAction &action = actions[i];
+        if (action.event)
+            continue;
+        if (action.specifiedObject == d->target && action.property.name() == QLatin1String("x")) {
+            data->toX = action.toValue.toReal();
+            modified << action.property;
+            action.fromValue = action.toValue;
+        }
+        if (action.specifiedObject == d->target && action.property.name() == QLatin1String("y")) {
+            data->toY = action.toValue.toReal();
+            modified << action.property;
+            action.fromValue = action.toValue;
+        }
+    }
+
+    if (d->target && d->path &&
+        (modified.count() > origModifiedSize || data->fromDefined)) {
+        data->target = d->target;
+        data->path = d->path;
+        if (!d->rangeIsSet) {
+            d->pa->setStartValue(qreal(0));
+            d->pa->setEndValue(qreal(1));
+            d->rangeIsSet = true;
+        }
+        d->pa->setFromSourcedValue(&data->fromSourced);
+        d->pa->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped);
+    } else {
+        d->pa->setFromSourcedValue(0);
+        d->pa->setAnimValue(0, QAbstractAnimation::DeleteWhenStopped);
+        delete data;
+    }
+}
+
+void QSGPathAnimationUpdater::setValue(qreal v)
+{
+    if (!fromSourced && !fromDefined) { //### check if !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();
+        qreal endY = reverse ? target->y() + anchorPoint.y() : toY + anchorPoint.y();
+
+        prevBez.isValid = false;
+        painterPath = path->createPath(QPointF(startX, startY), QPointF(endX, endY), QStringList(), pathLength, attributePoints);
+        fromSourced = true;
+    }
+
+    qreal angle;
+    bool fixed = orientation == QSGPathAnimation::Fixed;
+    QPointF currentPos = !painterPath.isEmpty() ? path->sequentialPointAt(painterPath, pathLength, attributePoints, prevBez, v, fixed ? 0 : &angle) : path->sequentialPointAt(v, fixed ? 0 : &angle);
+
+    //adjust position according to anchor point
+    if (!anchorPoint.isNull()) {
+        currentPos -= anchorPoint;
+        if ((reverse && v == 1.0) || (!reverse && v == 0.0)) {
+            if (!anchorPoint.isNull() && !fixed)
+                target->setTransformOriginPoint(anchorPoint);
+        }
+    }
+
+    //### too expensive to reconstruct properties each time?
+    QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, "x"), currentPos.x(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
+    QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, "y"), currentPos.y(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
+
+    //adjust angle according to orientation
+    if (!fixed) {
+        switch (orientation) {
+            case QSGPathAnimation::RightFirst:
+                angle = -angle;
+                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;
+        }
+        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());
+//    }
+}
+
 QT_END_NAMESPACE
index ee3fe8d..6406b86 100644 (file)
@@ -122,10 +122,72 @@ protected:
     virtual QAbstractAnimation *qtAnimation();
 };
 
+class QSGItem;
+class QDeclarativePath;
+class QSGPathAnimationPrivate;
+class Q_AUTOTEST_EXPORT QSGPathAnimation : public QDeclarativeAbstractAnimation
+{
+    Q_OBJECT
+    Q_DECLARE_PRIVATE(QSGPathAnimation)
+
+    Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged)
+    Q_PROPERTY(QEasingCurve easing READ easing WRITE setEasing NOTIFY easingChanged)
+    Q_PROPERTY(QDeclarativePath *path READ path WRITE setPath NOTIFY pathChanged)
+    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)
+
+public:
+    QSGPathAnimation(QObject *parent=0);
+    virtual ~QSGPathAnimation();
+
+    enum Orientation {
+        Fixed,
+        RightFirst,
+        LeftFirst,
+        BottomFirst,
+        TopFirst
+    };
+    Q_ENUMS(Orientation)
+
+    int duration() const;
+    void setDuration(int);
+
+    QEasingCurve easing() const;
+    void setEasing(const QEasingCurve &);
+
+    QDeclarativePath *path() const;
+    void setPath(QDeclarativePath *);
+
+    QSGItem *target() const;
+    void setTarget(QSGItem *);
+
+    Orientation orientation() const;
+    void setOrientation(Orientation orientation);
+
+    QPointF anchorPoint() const;
+    void setAnchorPoint(const QPointF &point);
+
+protected:
+    virtual void transition(QDeclarativeStateActions &actions,
+                            QDeclarativeProperties &modified,
+                            TransitionDirection direction);
+    virtual QAbstractAnimation *qtAnimation();
+
+Q_SIGNALS:
+    void durationChanged(int);
+    void easingChanged(const QEasingCurve &);
+    void pathChanged();
+    void targetChanged();
+    void orientationChanged(Orientation);
+    void anchorPointChanged(const QPointF &);
+};
+
 QT_END_NAMESPACE
 
 QML_DECLARE_TYPE(QSGParentAnimation)
 QML_DECLARE_TYPE(QSGAnchorAnimation)
+QML_DECLARE_TYPE(QSGPathAnimation)
 
 QT_END_HEADER
 
index 6f26f35..e26dbbd 100644 (file)
@@ -56,6 +56,7 @@
 
 #include "qsganimation_p.h"
 
+#include <private/qdeclarativepath_p.h>
 #include <private/qdeclarativeanimation_p_p.h>
 
 QT_BEGIN_NAMESPACE
@@ -92,6 +93,46 @@ public:
     QList<QSGItem*> targets;
 };
 
+class QSGPathAnimationUpdater : public QDeclarativeBulkValueUpdater
+{
+public:
+    QDeclarativePath *path;
+
+    QPainterPath painterPath;
+    QDeclarativeCachedBezier prevBez;
+    qreal pathLength;
+    QList<QDeclarativePath::AttributePoint> attributePoints;
+
+    QSGItem *target;
+    bool reverse;
+    bool fromSourced;
+    bool fromDefined;
+    qreal toX;
+    qreal toY;
+    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);
+};
+
+class QSGPathAnimationPrivate : public QDeclarativeAbstractAnimationPrivate
+{
+    Q_DECLARE_PUBLIC(QSGPathAnimation)
+public:
+    QSGPathAnimationPrivate() : path(0), target(0),
+        rangeIsSet(false), orientation(QSGPathAnimation::Fixed), pa(0) {}
+
+    QDeclarativePath *path;
+    QSGItem *target;
+    bool rangeIsSet;
+    QSGPathAnimation::Orientation orientation;
+    QPointF anchorPoint;
+    QDeclarativeBulkValueAnimator *pa;
+};
+
+
 QT_END_NAMESPACE
 
 #endif // QSGANIMATION_P_H
index 845617c..3b1974c 100644 (file)
@@ -1701,7 +1701,7 @@ void QSGCanvasPrivate::updateDirtyNode(QSGItem *item)
             itemPriv->transforms.at(ii)->applyTo(&matrix);
 
         if (itemPriv->scale != 1. || itemPriv->rotation != 0.) {
-            QPointF origin = itemPriv->computeTransformOrigin();
+            QPointF origin = item->transformOriginPoint();
             matrix.translate(origin.x(), origin.y());
             if (itemPriv->scale != 1.)
                 matrix.scale(itemPriv->scale, itemPriv->scale);
index c2abc46..fd1cf66 100644 (file)
@@ -3458,9 +3458,21 @@ void QSGItem::setTransformOrigin(TransformOrigin origin)
 QPointF QSGItem::transformOriginPoint() const
 {
     Q_D(const QSGItem);
+    if (!d->transformOriginPoint.isNull())
+        return d->transformOriginPoint;
     return d->computeTransformOrigin();
 }
 
+void QSGItem::setTransformOriginPoint(const QPointF &point)
+{
+    Q_D(QSGItem);
+    if (d->transformOriginPoint == point)
+        return;
+
+    d->transformOriginPoint = point;
+    d->dirty(QSGItemPrivate::TransformOrigin);
+}
+
 qreal QSGItem::z() const
 {
     Q_D(const QSGItem);
index 249a206..9a540d7 100644 (file)
@@ -231,6 +231,7 @@ public:
     TransformOrigin transformOrigin() const;
     void setTransformOrigin(TransformOrigin);
     QPointF transformOriginPoint() const;
+    void setTransformOriginPoint(const QPointF &);
 
     qreal z() const;
     void setZ(qreal);
index a3acb97..efe61ce 100644 (file)
@@ -300,6 +300,8 @@ public:
     Qt::MouseButtons acceptedMouseButtons;
     Qt::InputMethodHints imHints;
 
+    QPointF transformOriginPoint;
+
     virtual qreal getImplicitWidth() const;
     virtual qreal getImplicitHeight() const;
     virtual void implicitWidthChanged();
index bca0437..8eb7cb3 100644 (file)
@@ -61,6 +61,7 @@
 #include "qsggridview_p.h"
 #include "qsgpathview_p.h"
 #include <private/qdeclarativepath_p.h>
+#include <private/qdeclarativepathinterpolator_p.h>
 #include "qsgpositioners_p.h"
 #include "qsgrepeater_p.h"
 #include "qsgloader_p.h"
@@ -125,6 +126,9 @@ static void qt_sgitems_defineModule(const char *uri, int major, int minor)
     qmlRegisterType<QDeclarativePathLine>(uri,major,minor,"PathLine");
     qmlRegisterType<QDeclarativePathPercent>(uri,major,minor,"PathPercent");
     qmlRegisterType<QDeclarativePathQuad>(uri,major,minor,"PathQuad");
+    qmlRegisterType<QDeclarativePathCatmullRomCurve>("QtQuick",2,0,"PathCurve");
+    qmlRegisterType<QDeclarativePathArc>("QtQuick",2,0,"PathArc");
+    qmlRegisterType<QDeclarativePathSvg>("QtQuick",2,0,"PathSvg");
     qmlRegisterType<QSGPathView>(uri,major,minor,"PathView");
     qmlRegisterUncreatableType<QSGBasePositioner>(uri,major,minor,"Positioner","Positioner is an abstract type that is only available as an attached property.");
 #ifndef QT_NO_VALIDATOR
@@ -191,6 +195,8 @@ static void qt_sgitems_defineModule(const char *uri, int major, int minor)
     qmlRegisterType<QSGAnchorSet>();
     qmlRegisterType<QSGAnchorAnimation>(uri, major, minor,"AnchorAnimation");
     qmlRegisterType<QSGParentAnimation>(uri, major, minor,"ParentAnimation");
+    qmlRegisterType<QSGPathAnimation>("QtQuick",2,0,"PathAnimation");
+    qmlRegisterType<QDeclarativePathInterpolator>("QtQuick",2,0,"PathInterpolator");
 
     qmlRegisterType<QSGDragTarget>("QtQuick", 2, 0, "DragTarget");
     qmlRegisterType<QSGDragTargetEvent>();
index 34752b5..57387f5 100644 (file)
@@ -41,6 +41,7 @@
 
 #include "private/qdeclarativepath_p.h"
 #include "private/qdeclarativepath_p_p.h"
+#include "private/qdeclarativesvgparser_p.h"
 
 #include <QSet>
 #include <QTime>
@@ -97,35 +98,47 @@ QDeclarativePath::~QDeclarativePath()
 qreal QDeclarativePath::startX() const
 {
     Q_D(const QDeclarativePath);
-    return d->startX;
+    return d->startX.isNull ? 0 : d->startX.value;
 }
 
 void QDeclarativePath::setStartX(qreal x)
 {
     Q_D(QDeclarativePath);
-    if (qFuzzyCompare(x, d->startX))
+    if (d->startX.isValid() && qFuzzyCompare(x, d->startX))
         return;
     d->startX = x;
     emit startXChanged();
     processPath();
 }
 
+bool QDeclarativePath::hasStartX() const
+{
+    Q_D(const QDeclarativePath);
+    return d->startX.isValid();
+}
+
 qreal QDeclarativePath::startY() const
 {
     Q_D(const QDeclarativePath);
-    return d->startY;
+    return d->startY.isNull ? 0 : d->startY.value;
 }
 
 void QDeclarativePath::setStartY(qreal y)
 {
     Q_D(QDeclarativePath);
-    if (qFuzzyCompare(y, d->startY))
+    if (d->startY.isValid() && qFuzzyCompare(y, d->startY))
         return;
     d->startY = y;
     emit startYChanged();
     processPath();
 }
 
+bool QDeclarativePath::hasStartY() const
+{
+    Q_D(const QDeclarativePath);
+    return d->startY.isValid();
+}
+
 /*!
     \qmlproperty bool QtQuick2::Path::closed
     This property holds whether the start and end of the path are identical.
@@ -163,6 +176,11 @@ QDeclarativeListProperty<QDeclarativePathElement> QDeclarativePath::pathElements
 void QDeclarativePath::interpolate(int idx, const QString &name, qreal value)
 {
     Q_D(QDeclarativePath);
+    interpolate(d->_attributePoints, idx, name, value);
+}
+
+void QDeclarativePath::interpolate(QList<AttributePoint> &attributePoints, int idx, const QString &name, qreal value)
+{
     if (!idx)
         return;
 
@@ -170,7 +188,7 @@ void QDeclarativePath::interpolate(int idx, const QString &name, qreal value)
     qreal lastPercent = 0;
     int search = idx - 1;
     while(search >= 0) {
-        const AttributePoint &point = d->_attributePoints.at(search);
+        const AttributePoint &point = attributePoints.at(search);
         if (point.values.contains(name)) {
             lastValue = point.values.value(name);
             lastPercent = point.origpercent;
@@ -181,10 +199,10 @@ void QDeclarativePath::interpolate(int idx, const QString &name, qreal value)
 
     ++search;
 
-    const AttributePoint &curPoint = d->_attributePoints.at(idx);
+    const AttributePoint &curPoint = attributePoints.at(idx);
 
     for (int ii = search; ii < idx; ++ii) {
-        AttributePoint &point = d->_attributePoints[ii];
+        AttributePoint &point = attributePoints[ii];
 
         qreal val = lastValue + (value - lastValue) * (point.origpercent - lastPercent) / (curPoint.origpercent - lastPercent);
         point.values.insert(name, val);
@@ -208,6 +226,22 @@ void QDeclarativePath::endpoint(const QString &name)
     }
 }
 
+void QDeclarativePath::endpoint(QList<AttributePoint> &attributePoints, const QString &name)
+{
+    const AttributePoint &first = attributePoints.first();
+    qreal val = first.values.value(name);
+    for (int ii = attributePoints.count() - 1; ii >= 0; ii--) {
+        const AttributePoint &point = attributePoints.at(ii);
+        if (point.values.contains(name)) {
+            for (int jj = ii + 1; jj < attributePoints.count(); ++jj) {
+                AttributePoint &setPoint = attributePoints[jj];
+                setPoint.values.insert(name, val);
+            }
+            return;
+        }
+    }
+}
+
 static QString percentString(QStringLiteral("_qfx_percent"));
 
 void QDeclarativePath::processPath()
@@ -218,42 +252,64 @@ void QDeclarativePath::processPath()
         return;
 
     d->_pointCache.clear();
-    d->_attributePoints.clear();
-    d->_path = QPainterPath();
+    d->prevBez.isValid = false;
+
+    d->_path = createPath(QPointF(), QPointF(), d->_attributes, d->pathLength, d->_attributePoints, &d->closed);
+
+    emit changed();
+}
+
+QPainterPath QDeclarativePath::createPath(const QPointF &startPoint, const QPointF &endPoint, const QStringList &attributes, qreal &pathLength, QList<AttributePoint> &attributePoints, bool *closed)
+{
+    Q_D(QDeclarativePath);
+
+    pathLength = 0;
+    attributePoints.clear();
+
+    if (!d->componentComplete)
+        return QPainterPath();
+
+    QPainterPath path;
 
     AttributePoint first;
-    for (int ii = 0; ii < d->_attributes.count(); ++ii)
-        first.values[d->_attributes.at(ii)] = 0;
-    d->_attributePoints << first;
+    for (int ii = 0; ii < attributes.count(); ++ii)
+        first.values[attributes.at(ii)] = 0;
+    attributePoints << first;
 
-    d->_path.moveTo(d->startX, d->startY);
+    qreal startX = d->startX.isValid() ? d->startX.value : startPoint.x();
+    qreal startY = d->startY.isValid() ? d->startY.value : startPoint.y();
+    path.moveTo(startX, startY);
 
-    QDeclarativeCurve *lastCurve = 0;
     bool usesPercent = false;
+    int index = 0;
     foreach (QDeclarativePathElement *pathElement, d->_pathElements) {
         if (QDeclarativeCurve *curve = qobject_cast<QDeclarativeCurve *>(pathElement)) {
-            curve->addToPath(d->_path);
+            QDeclarativePathData data;
+            data.index = index;
+            data.endPoint = endPoint;
+            data.curves = d->_pathCurves;
+            curve->addToPath(path, data);
             AttributePoint p;
-            p.origpercent = d->_path.length();
-            d->_attributePoints << p;
-            lastCurve = curve;
+            p.origpercent = path.length();
+            attributePoints << p;
+            ++index;
         } else if (QDeclarativePathAttribute *attribute = qobject_cast<QDeclarativePathAttribute *>(pathElement)) {
-            AttributePoint &point = d->_attributePoints.last();
+            AttributePoint &point = attributePoints.last();
             point.values[attribute->name()] = attribute->value();
-            interpolate(d->_attributePoints.count() - 1, attribute->name(), attribute->value());
+            interpolate(attributePoints, attributePoints.count() - 1, attribute->name(), attribute->value());
         } else if (QDeclarativePathPercent *percent = qobject_cast<QDeclarativePathPercent *>(pathElement)) {
-            AttributePoint &point = d->_attributePoints.last();
+            AttributePoint &point = attributePoints.last();
             point.values[percentString] = percent->value();
-            interpolate(d->_attributePoints.count() - 1, percentString, percent->value());
+            interpolate(attributePoints, attributePoints.count() - 1, percentString, percent->value());
             usesPercent = true;
         }
     }
 
     // Fixup end points
-    const AttributePoint &last = d->_attributePoints.last();
-    for (int ii = 0; ii < d->_attributes.count(); ++ii) {
-        if (!last.values.contains(d->_attributes.at(ii)))
-            endpoint(d->_attributes.at(ii));
+    const AttributePoint &last = attributePoints.last();
+    for (int ii = 0; ii < attributes.count(); ++ii) {
+        if (!last.values.contains(attributes.at(ii)))
+            endpoint(attributePoints, attributes.at(ii));
     }
     if (usesPercent && !last.values.contains(percentString)) {
         d->_attributePoints.last().values[percentString] = 1;
@@ -262,30 +318,34 @@ void QDeclarativePath::processPath()
 
 
     // Adjust percent
-    qreal length = d->_path.length();
+    qreal length = path.length();
     qreal prevpercent = 0;
     qreal prevorigpercent = 0;
-    for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
-        const AttributePoint &point = d->_attributePoints.at(ii);
+    for (int ii = 0; ii < attributePoints.count(); ++ii) {
+        const AttributePoint &point = attributePoints.at(ii);
         if (point.values.contains(percentString)) { //special string for QDeclarativePathPercent
             if ( ii > 0) {
-                qreal scale = (d->_attributePoints[ii].origpercent/length - prevorigpercent) /
+                qreal scale = (attributePoints[ii].origpercent/length - prevorigpercent) /
                             (point.values.value(percentString)-prevpercent);
-                d->_attributePoints[ii].scale = scale;
+                attributePoints[ii].scale = scale;
             }
-            d->_attributePoints[ii].origpercent /= length;
-            d->_attributePoints[ii].percent = point.values.value(percentString);
-            prevorigpercent = d->_attributePoints[ii].origpercent;
-            prevpercent = d->_attributePoints[ii].percent;
+            attributePoints[ii].origpercent /= length;
+            attributePoints[ii].percent = point.values.value(percentString);
+            prevorigpercent = attributePoints[ii].origpercent;
+            prevpercent = attributePoints[ii].percent;
         } else {
-            d->_attributePoints[ii].origpercent /= length;
-            d->_attributePoints[ii].percent = d->_attributePoints[ii].origpercent;
+            attributePoints[ii].origpercent /= length;
+            attributePoints[ii].percent = attributePoints[ii].origpercent;
         }
     }
 
-    d->closed = lastCurve && d->startX == lastCurve->x() && d->startY == lastCurve->y();
+    if (closed) {
+        QPointF end = path.currentPosition();
+        *closed = length > 0 && startX == end.x() && startY == end.y();
+    }
+    pathLength = length;
 
-    emit changed();
+    return path;
 }
 
 void QDeclarativePath::classBegin()
@@ -302,8 +362,11 @@ void QDeclarativePath::componentComplete()
 
     // First gather up all the attributes
     foreach (QDeclarativePathElement *pathElement, d->_pathElements) {
-        if (QDeclarativePathAttribute *attribute =
-            qobject_cast<QDeclarativePathAttribute *>(pathElement))
+        if (QDeclarativeCurve *curve =
+            qobject_cast<QDeclarativeCurve *>(pathElement))
+            d->_pathCurves.append(curve);
+        else if (QDeclarativePathAttribute *attribute =
+                 qobject_cast<QDeclarativePathAttribute *>(pathElement))
             attrs.insert(attribute->name());
     }
     d->_attributes = attrs.toList();
@@ -337,10 +400,10 @@ QStringList QDeclarativePath::attributes() const
     return d->_attributes;
 }
 
-static inline QBezier nextBezier(const QPainterPath &path, int *from, qreal *bezLength)
+static inline QBezier nextBezier(const QPainterPath &path, int *from, qreal *bezLength, bool reverse = false)
 {
-    const int lastElement = path.elementCount() - 1;
-    for (int i=*from; i <= lastElement; ++i) {
+    const int lastElement = reverse ? 0 : path.elementCount() - 1;
+    for (int i=*from; reverse ? i >= lastElement : i <= lastElement; reverse ? --i : ++i) {
         const QPainterPath::Element &e = path.elementAt(i);
 
         switch (e.type) {
@@ -352,7 +415,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 = i+1;
+            *from = reverse ? i-1 : i+1;
             return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
         }
         case QPainterPath::CurveToElement:
@@ -362,7 +425,7 @@ static inline QBezier nextBezier(const QPainterPath &path, int *from, qreal *bez
                                             path.elementAt(i+1),
                                             path.elementAt(i+2));
             *bezLength = b.length();
-            *from = i+3;
+            *from = reverse ? i-1 : i+3;
             return b;
         }
         default:
@@ -374,10 +437,16 @@ static inline QBezier nextBezier(const QPainterPath &path, int *from, qreal *bez
     return QBezier();
 }
 
+//derivative of the equation
+static inline qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d)
+{
+    return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
+}
+
 void QDeclarativePath::createPointCache() const
 {
     Q_D(const QDeclarativePath);
-    qreal pathLength = d->_path.length();
+    qreal pathLength = d->pathLength;
     if (pathLength <= 0 || qIsNaN(pathLength))
         return;
     // more points means less jitter between items as they move along the
@@ -426,6 +495,140 @@ void QDeclarativePath::createPointCache() const
     }
 }
 
+QPointF QDeclarativePath::sequentialPointAt(qreal p, qreal *angle) const
+{
+    Q_D(const QDeclarativePath);
+    return sequentialPointAt(d->_path, d->pathLength, d->_attributePoints, d->prevBez, p, angle);
+}
+
+QPointF QDeclarativePath::sequentialPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &prevBez, qreal p, qreal *angle)
+{
+    if (!prevBez.isValid)
+        return p > .5 ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
+                        forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
+
+    return p < prevBez.p ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
+                           forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
+}
+
+QPointF QDeclarativePath::forwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &prevBez, qreal p, qreal *angle)
+{
+    if (pathLength <= 0 || qIsNaN(pathLength))
+        return path.pointAtPercent(0);  //expensive?
+
+    const int lastElement = path.elementCount() - 1;
+    bool haveCachedBez = prevBez.isValid;
+    int currElement = haveCachedBez ? prevBez.element : 0;
+    qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
+    QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength);
+    qreal currLength = haveCachedBez ? prevBez.currLength : bezLength;
+    qreal epc = currLength / pathLength;
+
+    //find which set we are in
+    qreal prevPercent = 0;
+    qreal prevOrigPercent = 0;
+    for (int ii = 0; ii < attributePoints.count(); ++ii) {
+        qreal percent = p;
+        const AttributePoint &point = attributePoints.at(ii);
+        if (percent < point.percent || ii == attributePoints.count() - 1) {
+            qreal elementPercent = (percent - prevPercent);
+
+            qreal spc = prevOrigPercent + elementPercent * point.scale;
+
+            while (spc > epc) {
+                if (currElement > lastElement)
+                    break;
+                currBez = nextBezier(path, &currElement, &bezLength);
+                /*if (bezLength == 0.0) {
+                    currLength = pathLength;
+                    epc = 1.0;
+                    break;
+                }*/
+                currLength += bezLength;
+                epc = currLength / pathLength;
+            }
+            prevBez.element = currElement;
+            prevBez.bezLength = bezLength;
+            prevBez.currLength = currLength;
+            prevBez.bezier = currBez;
+            prevBez.p = p;
+            prevBez.isValid = true;
+
+            qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
+
+            if (angle) {
+                qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
+                qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
+                *angle = QLineF(0, 0, m1, m2).angle();
+            }
+
+            return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
+        }
+        prevOrigPercent = point.origpercent;
+        prevPercent = point.percent;
+    }
+
+    return QPointF(0,0);
+}
+
+//ideally this should be merged with forwardsPointAt
+QPointF QDeclarativePath::backwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &prevBez, qreal p, qreal *angle)
+{
+    if (pathLength <= 0 || qIsNaN(pathLength))
+        return path.pointAtPercent(0);
+
+    const int firstElement = 0;
+    bool haveCachedBez = prevBez.isValid;
+    int currElement = haveCachedBez ? prevBez.element : path.elementCount() - 1;
+    qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
+    QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength, true /*reverse*/);
+    qreal currLength = haveCachedBez ? prevBez.currLength : pathLength;
+    qreal prevLength = currLength - bezLength;
+    qreal epc = prevLength / pathLength;
+
+    for (int ii = attributePoints.count() - 1; ii > 0; --ii) {
+        qreal percent = p;
+        const AttributePoint &point = attributePoints.at(ii);
+        const AttributePoint &prevPoint = attributePoints.at(ii-1);
+        if (percent > prevPoint.percent || ii == 1) {
+            qreal elementPercent = (percent - prevPoint.percent);
+
+            qreal spc = prevPoint.origpercent + elementPercent * point.scale;
+
+            while (spc < epc) {
+                if (currElement < firstElement)
+                    break;
+                currBez = nextBezier(path, &currElement, &bezLength, true /*reverse*/);
+                /*if (bezLength == 0.0) {
+                    currLength = 0;
+                    epc = 0.0;
+                    break;
+                }*/
+                currLength = prevLength;
+                epc = (currLength - bezLength) / pathLength;
+            }
+            prevBez.element = currElement;
+            prevBez.bezLength = bezLength;
+            prevBez.currLength = currLength;
+            prevBez.bezier = currBez;
+            prevBez.p = p;
+            prevBez.isValid = true;
+
+            qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
+
+            if (angle) {
+                qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
+                qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
+                *angle = QLineF(0, 0, m1, m2).angle();
+            }
+
+            return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
+        }
+    }
+
+    return QPointF(0,0);
+}
+
 QPointF QDeclarativePath::pointAt(qreal p) const
 {
     Q_D(const QDeclarativePath);
@@ -472,32 +675,80 @@ qreal QDeclarativePath::attributeAt(const QString &name, qreal percent) const
 
 qreal QDeclarativeCurve::x() const
 {
-    return _x;
+    return _x.isNull ? 0 : _x.value;
 }
 
 void QDeclarativeCurve::setX(qreal x)
 {
-    if (_x != x) {
+    if (_x.isNull || _x != x) {
         _x = x;
         emit xChanged();
         emit changed();
     }
 }
 
+bool QDeclarativeCurve::hasX()
+{
+    return _x.isValid();
+}
+
 qreal QDeclarativeCurve::y() const
 {
-    return _y;
+    return _y.isNull ? 0 : _y.value;
 }
 
 void QDeclarativeCurve::setY(qreal y)
 {
-    if (_y != y) {
+    if (_y.isNull || _y != y) {
         _y = y;
         emit yChanged();
         emit changed();
     }
 }
 
+bool QDeclarativeCurve::hasY()
+{
+    return _y.isValid();
+}
+
+qreal QDeclarativeCurve::relativeX() const
+{
+    return _relativeX;
+}
+
+void QDeclarativeCurve::setRelativeX(qreal x)
+{
+    if (_relativeX.isNull || _relativeX != x) {
+        _relativeX = x;
+        emit relativeXChanged();
+        emit changed();
+    }
+}
+
+bool QDeclarativeCurve::hasRelativeX()
+{
+    return _relativeX.isValid();
+}
+
+qreal QDeclarativeCurve::relativeY() const
+{
+    return _relativeY;
+}
+
+void QDeclarativeCurve::setRelativeY(qreal y)
+{
+    if (_relativeY.isNull || _relativeY != y) {
+        _relativeY = y;
+        emit relativeYChanged();
+        emit changed();
+    }
+}
+
+bool QDeclarativeCurve::hasRelativeY()
+{
+    return _relativeY.isValid();
+}
+
 /****************************************************************************/
 
 /*!
@@ -642,9 +893,17 @@ void QDeclarativePathAttribute::setValue(qreal value)
     Defines the end point of the line.
 */
 
-void QDeclarativePathLine::addToPath(QPainterPath &path)
+inline QPointF positionForCurve(const QDeclarativePathData &data, const QPointF &prevPoint)
 {
-    path.lineTo(x(), y());
+    QDeclarativeCurve *curve = data.curves.at(data.index);
+    bool isEnd = data.index == data.curves.size() - 1;
+    return QPointF(curve->hasRelativeX() ? prevPoint.x() + curve->relativeX() : !isEnd || curve->hasX() ? curve->x() : data.endPoint.x(),
+                   curve->hasRelativeY() ? prevPoint.y() + curve->relativeY() : !isEnd || curve->hasY() ? curve->y() : data.endPoint.y());
+}
+
+void QDeclarativePathLine::addToPath(QPainterPath &path, const QDeclarativePathData &data)
+{
+    path.lineTo(positionForCurve(data, path.currentPosition()));
 }
 
 /****************************************************************************/
@@ -720,9 +979,50 @@ void QDeclarativePathQuad::setControlY(qreal y)
     }
 }
 
-void QDeclarativePathQuad::addToPath(QPainterPath &path)
+qreal QDeclarativePathQuad::relativeControlX() const
+{
+    return _relativeControlX;
+}
+
+void QDeclarativePathQuad::setRelativeControlX(qreal x)
+{
+    if (_relativeControlX.isNull || _relativeControlX != x) {
+        _relativeControlX = x;
+        emit relativeControlXChanged();
+        emit changed();
+    }
+}
+
+bool QDeclarativePathQuad::hasRelativeControlX()
+{
+    return _relativeControlX.isValid();
+}
+
+qreal QDeclarativePathQuad::relativeControlY() const
+{
+    return _relativeControlY;
+}
+
+void QDeclarativePathQuad::setRelativeControlY(qreal y)
+{
+    if (_relativeControlY.isNull || _relativeControlY != y) {
+        _relativeControlY = y;
+        emit relativeControlYChanged();
+        emit changed();
+    }
+}
+
+bool QDeclarativePathQuad::hasRelativeControlY()
 {
-    path.quadTo(controlX(), controlY(), x(), y());
+    return _relativeControlY.isValid();
+}
+
+void QDeclarativePathQuad::addToPath(QPainterPath &path, const QDeclarativePathData &data)
+{
+    const QPointF &prevPoint = path.currentPosition();
+    QPointF controlPoint(hasRelativeControlX() ? prevPoint.x() + relativeControlX() : controlX(),
+                         hasRelativeControlY() ? prevPoint.y() + relativeControlY() : controlY());
+    path.quadTo(controlPoint, positionForCurve(data, path.currentPosition()));
 }
 
 /****************************************************************************/
@@ -828,9 +1128,256 @@ void QDeclarativePathCubic::setControl2Y(qreal y)
     }
 }
 
-void QDeclarativePathCubic::addToPath(QPainterPath &path)
+qreal QDeclarativePathCubic::relativeControl1X() const
+{
+    return _relativeControl1X;
+}
+
+void QDeclarativePathCubic::setRelativeControl1X(qreal x)
+{
+    if (_relativeControl1X.isNull || _relativeControl1X != x) {
+        _relativeControl1X = x;
+        emit relativeControl1XChanged();
+        emit changed();
+    }
+}
+
+bool QDeclarativePathCubic::hasRelativeControl1X()
+{
+    return _relativeControl1X.isValid();
+}
+
+qreal QDeclarativePathCubic::relativeControl1Y() const
+{
+    return _relativeControl1Y;
+}
+
+void QDeclarativePathCubic::setRelativeControl1Y(qreal y)
+{
+    if (_relativeControl1Y.isNull || _relativeControl1Y != y) {
+        _relativeControl1Y = y;
+        emit relativeControl1YChanged();
+        emit changed();
+    }
+}
+
+bool QDeclarativePathCubic::hasRelativeControl1Y()
+{
+    return _relativeControl1Y.isValid();
+}
+
+qreal QDeclarativePathCubic::relativeControl2X() const
+{
+    return _relativeControl2X;
+}
+
+void QDeclarativePathCubic::setRelativeControl2X(qreal x)
+{
+    if (_relativeControl2X.isNull || _relativeControl2X != x) {
+        _relativeControl2X = x;
+        emit relativeControl2XChanged();
+        emit changed();
+    }
+}
+
+bool QDeclarativePathCubic::hasRelativeControl2X()
+{
+    return _relativeControl2X.isValid();
+}
+
+qreal QDeclarativePathCubic::relativeControl2Y() const
+{
+    return _relativeControl2Y;
+}
+
+void QDeclarativePathCubic::setRelativeControl2Y(qreal y)
+{
+    if (_relativeControl2Y.isNull || _relativeControl2Y != y) {
+        _relativeControl2Y = y;
+        emit relativeControl2YChanged();
+        emit changed();
+    }
+}
+
+bool QDeclarativePathCubic::hasRelativeControl2Y()
+{
+    return _relativeControl2Y.isValid();
+}
+
+void QDeclarativePathCubic::addToPath(QPainterPath &path, const QDeclarativePathData &data)
+{
+    const QPointF &prevPoint = path.currentPosition();
+    QPointF controlPoint1(hasRelativeControl1X() ? prevPoint.x() + relativeControl1X() : control1X(),
+                          hasRelativeControl1Y() ? prevPoint.y() + relativeControl1Y() : control1Y());
+    QPointF controlPoint2(hasRelativeControl2X() ? prevPoint.x() + relativeControl2X() : control2X(),
+                          hasRelativeControl2Y() ? prevPoint.y() + relativeControl2Y() : control2Y());
+    path.cubicTo(controlPoint1, controlPoint2, positionForCurve(data, path.currentPosition()));
+}
+
+/****************************************************************************/
+
+inline QPointF previousPathPosition(const QPainterPath &path)
+{
+    int count = path.elementCount();
+    if (count < 1)
+        return QPointF();
+
+    int index = path.elementAt(count-1).type == QPainterPath::CurveToDataElement ? count - 4 : count - 2;
+    return index > -1 ? QPointF(path.elementAt(index)) : path.pointAtPercent(0);
+}
+
+void QDeclarativePathCatmullRomCurve::addToPath(QPainterPath &path, const QDeclarativePathData &data)
+{
+    //here we convert catmull-rom spline to bezier for use in QPainterPath.
+    //basic conversion algorithm:
+    //  catmull-rom points * inverse bezier matrix * catmull-rom matrix = bezier points
+    //each point in the catmull-rom spline produces a bezier endpoint + 2 control points
+    //calculations for each point use a moving window of 4 points
+    //  (previous 2 points + current point + next point)
+    QPointF prevFar, prev, point, next;
+
+    //get previous points
+    int index = data.index - 1;
+    QDeclarativeCurve *curve = index == -1 ? 0 : data.curves.at(index);
+    if (qobject_cast<QDeclarativePathCatmullRomCurve*>(curve)) {
+        prev = path.currentPosition();
+        prevFar = previousPathPosition(path);
+    } else
+        prevFar = prev = path.currentPosition();
+
+    //get current point
+    point = positionForCurve(data, path.currentPosition());
+
+    //get next point
+    index = data.index + 1;
+    if (index < data.curves.count() && qobject_cast<QDeclarativePathCatmullRomCurve*>(data.curves.at(index))) {
+        QDeclarativePathData nextData;
+        nextData.index = index;
+        nextData.endPoint = data.endPoint;
+        nextData.curves = data.curves;
+        next = positionForCurve(nextData, point);
+    } else
+        next = point;
+
+    /*
+        full conversion matrix (inverse bezier * catmull-rom):
+        0.000,  1.000,  0.000,  0.000,
+        -0.167,  1.000,  0.167,  0.000,
+        0.000,  0.167,  1.000, -0.167,
+        0.000,  0.000,  1.000,  0.000
+
+        conversion doesn't require full matrix multiplication,
+        so below we simplify
+    */
+    QPointF control1(prevFar.x() * qreal(-0.167) +
+                     prev.x() +
+                     point.x() * qreal(0.167),
+                     prevFar.y() * qreal(-0.167) +
+                     prev.y() +
+                     point.y() * qreal(0.167));
+
+    QPointF control2(prev.x() * qreal(0.167) +
+                     point.x() +
+                     next.x() * qreal(-0.167),
+                     prev.y() * qreal(0.167) +
+                     point.y() +
+                     next.y() * qreal(-0.167));
+
+    path.cubicTo(control1, control2, point);
+}
+
+/****************************************************************************/
+
+qreal QDeclarativePathArc::radiusX() const
+{
+    return _radiusX;
+}
+
+void QDeclarativePathArc::setRadiusX(qreal radius)
+{
+    if (_radiusX == radius)
+        return;
+
+    _radiusX = radius;
+    emit radiusXChanged();
+}
+
+qreal QDeclarativePathArc::radiusY() const
+{
+    return _radiusY;
+}
+
+void QDeclarativePathArc::setRadiusY(qreal radius)
+{
+    if (_radiusY == radius)
+        return;
+
+    _radiusY = radius;
+    emit radiusYChanged();
+}
+
+bool QDeclarativePathArc::useLargeArc() const
+{
+    return _useLargeArc;
+}
+
+void QDeclarativePathArc::setUseLargeArc(bool largeArc)
+{
+    if (_useLargeArc == largeArc)
+        return;
+
+    _useLargeArc = largeArc;
+    emit useLargeArcChanged();
+}
+
+QDeclarativePathArc::ArcDirection QDeclarativePathArc::direction() const
+{
+    return _direction;
+}
+
+void QDeclarativePathArc::setDirection(ArcDirection direction)
+{
+    if (_direction == direction)
+        return;
+
+    _direction = direction;
+    emit directionChanged();
+}
+
+void QDeclarativePathArc::addToPath(QPainterPath &path, const QDeclarativePathData &data)
+{
+    const QPointF &startPoint = path.currentPosition();
+    const QPointF &endPoint = positionForCurve(data, startPoint);
+    QDeclarativeSvgParser::pathArc(path,
+            _radiusX,
+            _radiusY,
+            0,  //xAxisRotation
+            _useLargeArc,
+            _direction == Clockwise ? 1 : 0,
+            endPoint.x(),
+            endPoint.y(),
+            startPoint.x(), startPoint.y());
+}
+
+/****************************************************************************/
+
+QString QDeclarativePathSvg::path() const
+{
+    return _path;
+}
+
+void QDeclarativePathSvg::setPath(const QString &path)
+{
+    if (_path == path)
+        return;
+
+    _path = path;
+    emit pathChanged();
+}
+
+void QDeclarativePathSvg::addToPath(QPainterPath &path, const QDeclarativePathData &)
 {
-    path.cubicTo(control1X(), control1Y(), control2X(), control2Y(), x(), y());
+    QDeclarativeSvgParser::parsePathDataFast(_path, path);
 }
 
 /****************************************************************************/
index c8420eb..4ce1bcf 100644 (file)
@@ -44,6 +44,9 @@
 
 #include <qdeclarative.h>
 
+#include "private/qdeclarativenullablevalue_p_p.h"
+#include <private/qbezier_p.h>
+
 #include <QtCore/QObject>
 #include <QtGui/QPainterPath>
 
@@ -52,6 +55,15 @@ QT_BEGIN_HEADER
 QT_BEGIN_NAMESPACE
 
 QT_MODULE(Declarative)
+
+class QDeclarativeCurve;
+struct QDeclarativePathData
+{
+    int index;
+    QPointF endPoint;
+    QList<QDeclarativeCurve*> curves;
+};
+
 class Q_AUTOTEST_EXPORT QDeclarativePathElement : public QObject
 {
     Q_OBJECT
@@ -92,24 +104,40 @@ class Q_AUTOTEST_EXPORT QDeclarativeCurve : public QDeclarativePathElement
 
     Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged)
     Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged)
+    Q_PROPERTY(qreal relativeX READ relativeX WRITE setRelativeX NOTIFY relativeXChanged)
+    Q_PROPERTY(qreal relativeY READ relativeY WRITE setRelativeY NOTIFY relativeYChanged)
 public:
-    QDeclarativeCurve(QObject *parent=0) : QDeclarativePathElement(parent), _x(0), _y(0) {}
+    QDeclarativeCurve(QObject *parent=0) : QDeclarativePathElement(parent) {}
 
     qreal x() const;
     void setX(qreal x);
+    bool hasX();
 
     qreal y() const;
     void setY(qreal y);
+    bool hasY();
+
+    qreal relativeX() const;
+    void setRelativeX(qreal x);
+    bool hasRelativeX();
+
+    qreal relativeY() const;
+    void setRelativeY(qreal y);
+    bool hasRelativeY();
 
-    virtual void addToPath(QPainterPath &) {}
+    virtual void addToPath(QPainterPath &, const QDeclarativePathData &) {}
 
 Q_SIGNALS:
     void xChanged();
     void yChanged();
+    void relativeXChanged();
+    void relativeYChanged();
 
 private:
-    qreal _x;
-    qreal _y;
+    QDeclarativeNullableValue<qreal> _x;
+    QDeclarativeNullableValue<qreal> _y;
+    QDeclarativeNullableValue<qreal> _relativeX;
+    QDeclarativeNullableValue<qreal> _relativeY;
 };
 
 class Q_AUTOTEST_EXPORT QDeclarativePathLine : public QDeclarativeCurve
@@ -118,7 +146,7 @@ class Q_AUTOTEST_EXPORT QDeclarativePathLine : public QDeclarativeCurve
 public:
     QDeclarativePathLine(QObject *parent=0) : QDeclarativeCurve(parent) {}
 
-    void addToPath(QPainterPath &path);
+    void addToPath(QPainterPath &path, const QDeclarativePathData &);
 };
 
 class Q_AUTOTEST_EXPORT QDeclarativePathQuad : public QDeclarativeCurve
@@ -127,6 +155,8 @@ class Q_AUTOTEST_EXPORT QDeclarativePathQuad : public QDeclarativeCurve
 
     Q_PROPERTY(qreal controlX READ controlX WRITE setControlX NOTIFY controlXChanged)
     Q_PROPERTY(qreal controlY READ controlY WRITE setControlY NOTIFY controlYChanged)
+    Q_PROPERTY(qreal relativeControlX READ relativeControlX WRITE setRelativeControlX NOTIFY relativeControlXChanged)
+    Q_PROPERTY(qreal relativeControlY READ relativeControlY WRITE setRelativeControlY NOTIFY relativeControlYChanged)
 public:
     QDeclarativePathQuad(QObject *parent=0) : QDeclarativeCurve(parent), _controlX(0), _controlY(0) {}
 
@@ -136,15 +166,27 @@ public:
     qreal controlY() const;
     void setControlY(qreal y);
 
-    void addToPath(QPainterPath &path);
+    qreal relativeControlX() const;
+    void setRelativeControlX(qreal x);
+    bool hasRelativeControlX();
+
+    qreal relativeControlY() const;
+    void setRelativeControlY(qreal y);
+    bool hasRelativeControlY();
+
+    void addToPath(QPainterPath &path, const QDeclarativePathData &);
 
 Q_SIGNALS:
     void controlXChanged();
     void controlYChanged();
+    void relativeControlXChanged();
+    void relativeControlYChanged();
 
 private:
     qreal _controlX;
     qreal _controlY;
+    QDeclarativeNullableValue<qreal> _relativeControlX;
+    QDeclarativeNullableValue<qreal> _relativeControlY;
 };
 
 class Q_AUTOTEST_EXPORT QDeclarativePathCubic : public QDeclarativeCurve
@@ -155,6 +197,10 @@ class Q_AUTOTEST_EXPORT QDeclarativePathCubic : public QDeclarativeCurve
     Q_PROPERTY(qreal control1Y READ control1Y WRITE setControl1Y NOTIFY control1YChanged)
     Q_PROPERTY(qreal control2X READ control2X WRITE setControl2X NOTIFY control2XChanged)
     Q_PROPERTY(qreal control2Y READ control2Y WRITE setControl2Y NOTIFY control2YChanged)
+    Q_PROPERTY(qreal relativeControl1X READ relativeControl1X WRITE setRelativeControl1X NOTIFY relativeControl1XChanged)
+    Q_PROPERTY(qreal relativeControl1Y READ relativeControl1Y WRITE setRelativeControl1Y NOTIFY relativeControl1YChanged)
+    Q_PROPERTY(qreal relativeControl2X READ relativeControl2X WRITE setRelativeControl2X NOTIFY relativeControl2XChanged)
+    Q_PROPERTY(qreal relativeControl2Y READ relativeControl2Y WRITE setRelativeControl2Y NOTIFY relativeControl2YChanged)
 public:
     QDeclarativePathCubic(QObject *parent=0) : QDeclarativeCurve(parent), _control1X(0), _control1Y(0), _control2X(0), _control2Y(0) {}
 
@@ -170,19 +216,113 @@ public:
     qreal control2Y() const;
     void setControl2Y(qreal y);
 
-    void addToPath(QPainterPath &path);
+    qreal relativeControl1X() const;
+    void setRelativeControl1X(qreal x);
+    bool hasRelativeControl1X();
+
+    qreal relativeControl1Y() const;
+    void setRelativeControl1Y(qreal y);
+    bool hasRelativeControl1Y();
+
+    qreal relativeControl2X() const;
+    void setRelativeControl2X(qreal x);
+    bool hasRelativeControl2X();
+
+    qreal relativeControl2Y() const;
+    void setRelativeControl2Y(qreal y);
+    bool hasRelativeControl2Y();
+
+    void addToPath(QPainterPath &path, const QDeclarativePathData &);
 
 Q_SIGNALS:
     void control1XChanged();
     void control1YChanged();
     void control2XChanged();
     void control2YChanged();
+    void relativeControl1XChanged();
+    void relativeControl1YChanged();
+    void relativeControl2XChanged();
+    void relativeControl2YChanged();
 
 private:
     qreal _control1X;
     qreal _control1Y;
     qreal _control2X;
     qreal _control2Y;
+    QDeclarativeNullableValue<qreal> _relativeControl1X;
+    QDeclarativeNullableValue<qreal> _relativeControl1Y;
+    QDeclarativeNullableValue<qreal> _relativeControl2X;
+    QDeclarativeNullableValue<qreal> _relativeControl2Y;
+};
+
+class Q_AUTOTEST_EXPORT QDeclarativePathCatmullRomCurve : public QDeclarativeCurve
+{
+    Q_OBJECT
+public:
+    QDeclarativePathCatmullRomCurve(QObject *parent=0) : QDeclarativeCurve(parent) {}
+
+    void addToPath(QPainterPath &path, const QDeclarativePathData &);
+};
+
+class Q_AUTOTEST_EXPORT QDeclarativePathArc : public QDeclarativeCurve
+{
+    Q_OBJECT
+    Q_PROPERTY(qreal radiusX READ radiusX WRITE setRadiusX NOTIFY radiusXChanged)
+    Q_PROPERTY(qreal radiusY READ radiusY WRITE setRadiusY NOTIFY radiusYChanged)
+    Q_PROPERTY(bool useLargeArc READ useLargeArc WRITE setUseLargeArc NOTIFY useLargeArcChanged)
+    Q_PROPERTY(ArcDirection direction READ direction WRITE setDirection NOTIFY directionChanged)
+
+public:
+    QDeclarativePathArc(QObject *parent=0)
+        : QDeclarativeCurve(parent), _radiusX(0), _radiusY(0), _useLargeArc(false), _direction(Clockwise) {}
+
+    enum ArcDirection { Clockwise, Counterclockwise };
+    Q_ENUMS(ArcDirection)
+
+    qreal radiusX() const;
+    void setRadiusX(qreal);
+
+    qreal radiusY() const;
+    void setRadiusY(qreal);
+
+    bool useLargeArc() const;
+    void setUseLargeArc(bool);
+
+    ArcDirection direction() const;
+    void setDirection(ArcDirection direction);
+
+    void addToPath(QPainterPath &path, const QDeclarativePathData &);
+
+Q_SIGNALS:
+    void radiusXChanged();
+    void radiusYChanged();
+    void useLargeArcChanged();
+    void directionChanged();
+
+private:
+    qreal _radiusX;
+    qreal _radiusY;
+    bool _useLargeArc;
+    ArcDirection _direction;
+};
+
+class Q_AUTOTEST_EXPORT QDeclarativePathSvg : public QDeclarativeCurve
+{
+    Q_OBJECT
+    Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
+public:
+    QDeclarativePathSvg(QObject *parent=0) : QDeclarativeCurve(parent) {}
+
+    QString path() const;
+    void setPath(const QString &path);
+
+    void addToPath(QPainterPath &path, const QDeclarativePathData &);
+
+Q_SIGNALS:
+    void pathChanged();
+
+private:
+    QString _path;
 };
 
 class Q_AUTOTEST_EXPORT QDeclarativePathPercent : public QDeclarativePathElement
@@ -202,6 +342,17 @@ private:
     qreal _value;
 };
 
+struct QDeclarativeCachedBezier
+{
+    QDeclarativeCachedBezier() : isValid(false) {}
+    QBezier bezier;
+    int element;
+    qreal bezLength;
+    qreal currLength;
+    qreal p;
+    bool isValid;
+};
+
 class QDeclarativePathPrivate;
 class Q_AUTOTEST_EXPORT QDeclarativePath : public QObject, public QDeclarativeParserStatus
 {
@@ -222,9 +373,11 @@ public:
 
     qreal startX() const;
     void setStartX(qreal x);
+    bool hasStartX() const;
 
     qreal startY() const;
     void setStartY(qreal y);
+    bool hasStartY() const;
 
     bool isClosed() const;
 
@@ -232,6 +385,7 @@ public:
     QStringList attributes() const;
     qreal attributeAt(const QString &, qreal) const;
     QPointF pointAt(qreal) const;
+    QPointF sequentialPointAt(qreal p, qreal *angle = 0) const;
 
 Q_SIGNALS:
     void changed();
@@ -263,9 +417,19 @@ private:
     void endpoint(const QString &name);
     void createPointCache() const;
 
+    static void interpolate(QList<AttributePoint> &points, int idx, const QString &name, qreal value);
+    static void endpoint(QList<AttributePoint> &attributePoints, const QString &name);
+    static QPointF forwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &prevBez, qreal p, qreal *angle = 0);
+    static QPointF backwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &prevBez, qreal p, qreal *angle = 0);
+
 private:
     Q_DISABLE_COPY(QDeclarativePath)
     Q_DECLARE_PRIVATE(QDeclarativePath)
+    friend class QSGPathAnimationUpdater;
+
+public:
+    QPainterPath createPath(const QPointF &startPoint, const QPointF &endPoint, const QStringList &attributes, qreal &pathLength, QList<AttributePoint> &attributePoints, bool *closed = 0);
+    static QPointF sequentialPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &prevBez, qreal p, qreal *angle = 0);
 };
 
 QT_END_NAMESPACE
@@ -276,6 +440,9 @@ QML_DECLARE_TYPE(QDeclarativeCurve)
 QML_DECLARE_TYPE(QDeclarativePathLine)
 QML_DECLARE_TYPE(QDeclarativePathQuad)
 QML_DECLARE_TYPE(QDeclarativePathCubic)
+QML_DECLARE_TYPE(QDeclarativePathCatmullRomCurve)
+QML_DECLARE_TYPE(QDeclarativePathArc)
+QML_DECLARE_TYPE(QDeclarativePathSvg)
 QML_DECLARE_TYPE(QDeclarativePathPercent)
 QML_DECLARE_TYPE(QDeclarativePath)
 
index 4e407ec..c7313ea 100644 (file)
 #include "private/qdeclarativepath_p.h"
 
 #include <qdeclarative.h>
+#include <QtCore/QStringList>
 
-#include <qstringlist.h>
 #include <private/qobject_p.h>
 
 QT_BEGIN_NAMESPACE
+
 class QDeclarativePathPrivate : public QObjectPrivate
 {
     Q_DECLARE_PUBLIC(QDeclarativePath)
 
 public:
-    QDeclarativePathPrivate() : startX(0), startY(0), closed(false), componentComplete(true) { }
+    QDeclarativePathPrivate() : pathLength(0), closed(false), componentComplete(true) { }
 
     QPainterPath _path;
     QList<QDeclarativePathElement*> _pathElements;
     mutable QVector<QPointF> _pointCache;
     QList<QDeclarativePath::AttributePoint> _attributePoints;
     QStringList _attributes;
-    int startX;
-    int startY;
+    QList<QDeclarativeCurve*> _pathCurves;
+    mutable QDeclarativeCachedBezier prevBez;
+    QDeclarativeNullableValue<qreal> startX;
+    QDeclarativeNullableValue<qreal> startY;
+    qreal pathLength;
     bool closed;
     bool componentComplete;
 };
 
 QT_END_NAMESPACE
+
 #endif
diff --git a/src/declarative/util/qdeclarativepathinterpolator.cpp b/src/declarative/util/qdeclarativepathinterpolator.cpp
new file mode 100644 (file)
index 0000000..db6ef15
--- /dev/null
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdeclarativepathinterpolator_p.h"
+
+#include "private/qdeclarativepath_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QDeclarativePathInterpolator::QDeclarativePathInterpolator(QObject *parent) :
+    QObject(parent), _path(0), _x(0), _y(0), _angle(0), _progress(0)
+{
+}
+
+QDeclarativePath *QDeclarativePathInterpolator::path() const
+{
+    return _path;
+}
+
+void QDeclarativePathInterpolator::setPath(QDeclarativePath *path)
+{
+    if (_path == path)
+        return;
+    if (_path)
+        disconnect(_path, SIGNAL(changed()), this, SLOT(_q_pathUpdated()));
+    _path = path;
+    connect(_path, SIGNAL(changed()), this, SLOT(_q_pathUpdated()));
+    emit pathChanged();
+}
+
+qreal QDeclarativePathInterpolator::progress() const
+{
+    return _progress;
+}
+
+void QDeclarativePathInterpolator::setProgress(qreal progress)
+{
+    if (progress == _progress)
+        return;
+    _progress = progress;
+    emit progressChanged();
+    _q_pathUpdated();
+}
+
+qreal QDeclarativePathInterpolator::x() const
+{
+    return _x;
+}
+
+qreal QDeclarativePathInterpolator::y() const
+{
+    return _y;
+}
+
+qreal QDeclarativePathInterpolator::angle() const
+{
+    return _angle;
+}
+
+void QDeclarativePathInterpolator::_q_pathUpdated()
+{
+    if (! _path)
+        return;
+
+    qreal angle = 0;
+    const QPointF pt = _path->sequentialPointAt(_progress, &angle);
+
+    if (_x != pt.x()) {
+        _x = pt.x();
+        emit xChanged();
+    }
+
+    if (_y != pt.y()) {
+        _y = pt.y();
+        emit yChanged();
+    }
+
+    if (angle != _angle) {
+        _angle = angle;
+        emit angleChanged();
+    }
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/util/qdeclarativepathinterpolator_p.h b/src/declarative/util/qdeclarativepathinterpolator_p.h
new file mode 100644 (file)
index 0000000..cb8ccfa
--- /dev/null
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECLARATIVEPATHINTERPOLATOR_P_H
+#define QDECLARATIVEPATHINTERPOLATOR_P_H
+
+#include <qdeclarative.h>
+#include <QObject>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QDeclarativePath;
+class Q_AUTOTEST_EXPORT QDeclarativePathInterpolator : public QObject
+{
+    Q_OBJECT
+    Q_PROPERTY(QDeclarativePath *path READ path WRITE setPath NOTIFY pathChanged)
+    Q_PROPERTY(qreal progress READ progress WRITE setProgress NOTIFY progressChanged)
+    Q_PROPERTY(qreal x READ x NOTIFY xChanged)
+    Q_PROPERTY(qreal y READ y NOTIFY yChanged)
+    Q_PROPERTY(qreal angle READ angle NOTIFY angleChanged)
+public:
+    explicit QDeclarativePathInterpolator(QObject *parent = 0);
+
+    QDeclarativePath *path() const;
+    void setPath(QDeclarativePath *path);
+
+    qreal progress() const;
+    void setProgress(qreal progress);
+
+    qreal x() const;
+    qreal y() const;
+    qreal angle() const;
+
+Q_SIGNALS:
+    void pathChanged();
+    void progressChanged();
+    void xChanged();
+    void yChanged();
+    void angleChanged();
+
+private Q_SLOTS:
+    void _q_pathUpdated();
+
+private:
+    QDeclarativePath *_path;
+    qreal _x;
+    qreal _y;
+    qreal _angle;
+    qreal _progress;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QDeclarativePathInterpolator)
+
+QT_END_HEADER
+
+#endif // QDECLARATIVEPATHINTERPOLATOR_P_H
diff --git a/src/declarative/util/qdeclarativesvgparser.cpp b/src/declarative/util/qdeclarativesvgparser.cpp
new file mode 100644 (file)
index 0000000..36714d7
--- /dev/null
@@ -0,0 +1,633 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclaractive module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdeclarativesvgparser_p.h"
+
+#include <QtCore/qmath.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+static const double Q_PI   = 3.14159265358979323846;   // pi
+
+//copied from QtSvg (qsvghandler.cpp).
+Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok);
+// '0' is 0x30 and '9' is 0x39
+static inline bool isDigit(ushort ch)
+{
+    static quint16 magic = 0x3ff;
+    return ((ch >> 4) == 3) && (magic >> (ch & 15));
+}
+
+static qreal toDouble(const QChar *&str)
+{
+    const int maxLen = 255;//technically doubles can go til 308+ but whatever
+    char temp[maxLen+1];
+    int pos = 0;
+
+    if (*str == QLatin1Char('-')) {
+        temp[pos++] = '-';
+        ++str;
+    } else if (*str == QLatin1Char('+')) {
+        ++str;
+    }
+    while (isDigit(str->unicode()) && pos < maxLen) {
+        temp[pos++] = str->toLatin1();
+        ++str;
+    }
+    if (*str == QLatin1Char('.') && pos < maxLen) {
+        temp[pos++] = '.';
+        ++str;
+    }
+    while (isDigit(str->unicode()) && pos < maxLen) {
+        temp[pos++] = str->toLatin1();
+        ++str;
+    }
+    bool exponent = false;
+    if ((*str == QLatin1Char('e') || *str == QLatin1Char('E')) && pos < maxLen) {
+        exponent = true;
+        temp[pos++] = 'e';
+        ++str;
+        if ((*str == QLatin1Char('-') || *str == QLatin1Char('+')) && pos < maxLen) {
+            temp[pos++] = str->toLatin1();
+            ++str;
+        }
+        while (isDigit(str->unicode()) && pos < maxLen) {
+            temp[pos++] = str->toLatin1();
+            ++str;
+        }
+    }
+
+    temp[pos] = '\0';
+
+    qreal val;
+    if (!exponent && pos < 10) {
+        int ival = 0;
+        const char *t = temp;
+        bool neg = false;
+        if(*t == '-') {
+            neg = true;
+            ++t;
+        }
+        while(*t && *t != '.') {
+            ival *= 10;
+            ival += (*t) - '0';
+            ++t;
+        }
+        if(*t == '.') {
+            ++t;
+            int div = 1;
+            while(*t) {
+                ival *= 10;
+                ival += (*t) - '0';
+                div *= 10;
+                ++t;
+            }
+            val = ((qreal)ival)/((qreal)div);
+        } else {
+            val = ival;
+        }
+        if (neg)
+            val = -val;
+    } else {
+#if defined(Q_WS_QWS) && !defined(Q_OS_VXWORKS)
+        if(sizeof(qreal) == sizeof(float))
+            val = strtof(temp, 0);
+        else
+#endif
+        {
+            bool ok = false;
+            val = qstrtod(temp, 0, &ok);
+        }
+    }
+    return val;
+
+}
+static qreal toDouble(const QString &str, bool *ok = NULL)
+{
+    const QChar *c = str.constData();
+    qreal res = toDouble(c);
+    if (ok) {
+        *ok = ((*c) == QLatin1Char('\0'));
+    }
+    return res;
+}
+
+static qreal toDouble(const QStringRef &str, bool *ok = NULL)
+{
+    const QChar *c = str.constData();
+    qreal res = toDouble(c);
+    if (ok) {
+        *ok = (c == (str.constData() + str.length()));
+    }
+    return res;
+}
+static inline void parseNumbersArray(const QChar *&str, QVarLengthArray<qreal, 8> &points)
+{
+    while (str->isSpace())
+        ++str;
+    while (isDigit(str->unicode()) ||
+           *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
+           *str == QLatin1Char('.')) {
+
+        points.append(toDouble(str));
+
+        while (str->isSpace())
+            ++str;
+        if (*str == QLatin1Char(','))
+            ++str;
+
+        //eat the rest of space
+        while (str->isSpace())
+            ++str;
+    }
+}
+
+static void pathArcSegment(QPainterPath &path,
+                           qreal xc, qreal yc,
+                           qreal th0, qreal th1,
+                           qreal rx, qreal ry, qreal xAxisRotation)
+{
+    qreal sinTh, cosTh;
+    qreal a00, a01, a10, a11;
+    qreal x1, y1, x2, y2, x3, y3;
+    qreal t;
+    qreal thHalf;
+
+    sinTh = qSin(xAxisRotation * (Q_PI / 180.0));
+    cosTh = qCos(xAxisRotation * (Q_PI / 180.0));
+
+    a00 =  cosTh * rx;
+    a01 = -sinTh * ry;
+    a10 =  sinTh * rx;
+    a11 =  cosTh * ry;
+
+    thHalf = 0.5 * (th1 - th0);
+    t = (8.0 / 3.0) * qSin(thHalf * 0.5) * qSin(thHalf * 0.5) / qSin(thHalf);
+    x1 = xc + qCos(th0) - t * qSin(th0);
+    y1 = yc + qSin(th0) + t * qCos(th0);
+    x3 = xc + qCos(th1);
+    y3 = yc + qSin(th1);
+    x2 = x3 + t * qSin(th1);
+    y2 = y3 - t * qCos(th1);
+
+    path.cubicTo(a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
+                 a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
+                 a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
+}
+
+void QDeclarativeSvgParser::pathArc(QPainterPath &path,
+                    qreal               rx,
+                    qreal               ry,
+                    qreal               x_axis_rotation,
+                    int         large_arc_flag,
+                    int         sweep_flag,
+                    qreal               x,
+                    qreal               y,
+                    qreal curx, qreal cury)
+{
+    qreal sin_th, cos_th;
+    qreal a00, a01, a10, a11;
+    qreal x0, y0, x1, y1, xc, yc;
+    qreal d, sfactor, sfactor_sq;
+    qreal th0, th1, th_arc;
+    int i, n_segs;
+    qreal dx, dy, dx1, dy1, Pr1, Pr2, Px, Py, check;
+
+    rx = qAbs(rx);
+    ry = qAbs(ry);
+
+    sin_th = qSin(x_axis_rotation * (Q_PI / 180.0));
+    cos_th = qCos(x_axis_rotation * (Q_PI / 180.0));
+
+    dx = (curx - x) / 2.0;
+    dy = (cury - y) / 2.0;
+    dx1 =  cos_th * dx + sin_th * dy;
+    dy1 = -sin_th * dx + cos_th * dy;
+    Pr1 = rx * rx;
+    Pr2 = ry * ry;
+    Px = dx1 * dx1;
+    Py = dy1 * dy1;
+    /* Spec : check if radii are large enough */
+    check = Px / Pr1 + Py / Pr2;
+    if (check > 1) {
+        rx = rx * qSqrt(check);
+        ry = ry * qSqrt(check);
+    }
+
+    a00 =  cos_th / rx;
+    a01 =  sin_th / rx;
+    a10 = -sin_th / ry;
+    a11 =  cos_th / ry;
+    x0 = a00 * curx + a01 * cury;
+    y0 = a10 * curx + a11 * cury;
+    x1 = a00 * x + a01 * y;
+    y1 = a10 * x + a11 * y;
+    /* (x0, y0) is current point in transformed coordinate space.
+       (x1, y1) is new point in transformed coordinate space.
+
+       The arc fits a unit-radius circle in this space.
+    */
+    d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
+    sfactor_sq = 1.0 / d - 0.25;
+    if (sfactor_sq < 0) sfactor_sq = 0;
+    sfactor = qSqrt(sfactor_sq);
+    if (sweep_flag == large_arc_flag) sfactor = -sfactor;
+    xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
+    yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
+    /* (xc, yc) is center of the circle. */
+
+    th0 = qAtan2(y0 - yc, x0 - xc);
+    th1 = qAtan2(y1 - yc, x1 - xc);
+
+    th_arc = th1 - th0;
+    if (th_arc < 0 && sweep_flag)
+        th_arc += 2 * Q_PI;
+    else if (th_arc > 0 && !sweep_flag)
+        th_arc -= 2 * Q_PI;
+
+    n_segs = qCeil(qAbs(th_arc / (Q_PI * 0.5 + 0.001)));
+
+    for (i = 0; i < n_segs; i++) {
+        pathArcSegment(path, xc, yc,
+                       th0 + i * th_arc / n_segs,
+                       th0 + (i + 1) * th_arc / n_segs,
+                       rx, ry, x_axis_rotation);
+    }
+}
+
+
+bool QDeclarativeSvgParser::parsePathDataFast(const QString &dataStr, QPainterPath &path)
+{
+    qreal x0 = 0, y0 = 0;              // starting point
+    qreal x = 0, y = 0;                // current point
+    char lastMode = 0;
+    QPointF ctrlPt;
+    const QChar *str = dataStr.constData();
+    const QChar *end = str + dataStr.size();
+
+    while (str != end) {
+        while (str->isSpace())
+            ++str;
+        QChar pathElem = *str;
+        ++str;
+        QChar endc = *end;
+        *const_cast<QChar *>(end) = 0; // parseNumbersArray requires 0-termination that QStringRef cannot guarantee
+        QVarLengthArray<qreal, 8> arg;
+        parseNumbersArray(str, arg);
+        *const_cast<QChar *>(end) = endc;
+        if (pathElem == QLatin1Char('z') || pathElem == QLatin1Char('Z'))
+            arg.append(0);//dummy
+        const qreal *num = arg.constData();
+        int count = arg.count();
+        while (count > 0) {
+            qreal offsetX = x;        // correction offsets
+            qreal offsetY = y;        // for relative commands
+            switch (pathElem.unicode()) {
+            case 'm': {
+                if (count < 2) {
+                    num++;
+                    count--;
+                    break;
+                }
+                x = x0 = num[0] + offsetX;
+                y = y0 = num[1] + offsetY;
+                num += 2;
+                count -= 2;
+                path.moveTo(x0, y0);
+
+                 // As per 1.2  spec 8.3.2 The "moveto" commands
+                 // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
+                 // the subsequent pairs shall be treated as implicit 'lineto' commands.
+                 pathElem = QLatin1Char('l');
+            }
+                break;
+            case 'M': {
+                if (count < 2) {
+                    num++;
+                    count--;
+                    break;
+                }
+                x = x0 = num[0];
+                y = y0 = num[1];
+                num += 2;
+                count -= 2;
+                path.moveTo(x0, y0);
+
+                // As per 1.2  spec 8.3.2 The "moveto" commands
+                // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
+                // the subsequent pairs shall be treated as implicit 'lineto' commands.
+                pathElem = QLatin1Char('L');
+            }
+                break;
+            case 'z':
+            case 'Z': {
+                x = x0;
+                y = y0;
+                count--; // skip dummy
+                num++;
+                path.closeSubpath();
+            }
+                break;
+            case 'l': {
+                if (count < 2) {
+                    num++;
+                    count--;
+                    break;
+                }
+                x = num[0] + offsetX;
+                y = num[1] + offsetY;
+                num += 2;
+                count -= 2;
+                path.lineTo(x, y);
+
+            }
+                break;
+            case 'L': {
+                if (count < 2) {
+                    num++;
+                    count--;
+                    break;
+                }
+                x = num[0];
+                y = num[1];
+                num += 2;
+                count -= 2;
+                path.lineTo(x, y);
+            }
+                break;
+            case 'h': {
+                x = num[0] + offsetX;
+                num++;
+                count--;
+                path.lineTo(x, y);
+            }
+                break;
+            case 'H': {
+                x = num[0];
+                num++;
+                count--;
+                path.lineTo(x, y);
+            }
+                break;
+            case 'v': {
+                y = num[0] + offsetY;
+                num++;
+                count--;
+                path.lineTo(x, y);
+            }
+                break;
+            case 'V': {
+                y = num[0];
+                num++;
+                count--;
+                path.lineTo(x, y);
+            }
+                break;
+            case 'c': {
+                if (count < 6) {
+                    num += count;
+                    count = 0;
+                    break;
+                }
+                QPointF c1(num[0] + offsetX, num[1] + offsetY);
+                QPointF c2(num[2] + offsetX, num[3] + offsetY);
+                QPointF e(num[4] + offsetX, num[5] + offsetY);
+                num += 6;
+                count -= 6;
+                path.cubicTo(c1, c2, e);
+                ctrlPt = c2;
+                x = e.x();
+                y = e.y();
+                break;
+            }
+            case 'C': {
+                if (count < 6) {
+                    num += count;
+                    count = 0;
+                    break;
+                }
+                QPointF c1(num[0], num[1]);
+                QPointF c2(num[2], num[3]);
+                QPointF e(num[4], num[5]);
+                num += 6;
+                count -= 6;
+                path.cubicTo(c1, c2, e);
+                ctrlPt = c2;
+                x = e.x();
+                y = e.y();
+                break;
+            }
+            case 's': {
+                if (count < 4) {
+                    num += count;
+                    count = 0;
+                    break;
+                }
+                QPointF c1;
+                if (lastMode == 'c' || lastMode == 'C' ||
+                    lastMode == 's' || lastMode == 'S')
+                    c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
+                else
+                    c1 = QPointF(x, y);
+                QPointF c2(num[0] + offsetX, num[1] + offsetY);
+                QPointF e(num[2] + offsetX, num[3] + offsetY);
+                num += 4;
+                count -= 4;
+                path.cubicTo(c1, c2, e);
+                ctrlPt = c2;
+                x = e.x();
+                y = e.y();
+                break;
+            }
+            case 'S': {
+                if (count < 4) {
+                    num += count;
+                    count = 0;
+                    break;
+                }
+                QPointF c1;
+                if (lastMode == 'c' || lastMode == 'C' ||
+                    lastMode == 's' || lastMode == 'S')
+                    c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
+                else
+                    c1 = QPointF(x, y);
+                QPointF c2(num[0], num[1]);
+                QPointF e(num[2], num[3]);
+                num += 4;
+                count -= 4;
+                path.cubicTo(c1, c2, e);
+                ctrlPt = c2;
+                x = e.x();
+                y = e.y();
+                break;
+            }
+            case 'q': {
+                if (count < 4) {
+                    num += count;
+                    count = 0;
+                    break;
+                }
+                QPointF c(num[0] + offsetX, num[1] + offsetY);
+                QPointF e(num[2] + offsetX, num[3] + offsetY);
+                num += 4;
+                count -= 4;
+                path.quadTo(c, e);
+                ctrlPt = c;
+                x = e.x();
+                y = e.y();
+                break;
+            }
+            case 'Q': {
+                if (count < 4) {
+                    num += count;
+                    count = 0;
+                    break;
+                }
+                QPointF c(num[0], num[1]);
+                QPointF e(num[2], num[3]);
+                num += 4;
+                count -= 4;
+                path.quadTo(c, e);
+                ctrlPt = c;
+                x = e.x();
+                y = e.y();
+                break;
+            }
+            case 't': {
+                if (count < 2) {
+                    num += count;
+                    count = 0;
+                    break;
+                }
+                QPointF e(num[0] + offsetX, num[1] + offsetY);
+                num += 2;
+                count -= 2;
+                QPointF c;
+                if (lastMode == 'q' || lastMode == 'Q' ||
+                    lastMode == 't' || lastMode == 'T')
+                    c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
+                else
+                    c = QPointF(x, y);
+                path.quadTo(c, e);
+                ctrlPt = c;
+                x = e.x();
+                y = e.y();
+                break;
+            }
+            case 'T': {
+                if (count < 2) {
+                    num += count;
+                    count = 0;
+                    break;
+                }
+                QPointF e(num[0], num[1]);
+                num += 2;
+                count -= 2;
+                QPointF c;
+                if (lastMode == 'q' || lastMode == 'Q' ||
+                    lastMode == 't' || lastMode == 'T')
+                    c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
+                else
+                    c = QPointF(x, y);
+                path.quadTo(c, e);
+                ctrlPt = c;
+                x = e.x();
+                y = e.y();
+                break;
+            }
+            case 'a': {
+                if (count < 7) {
+                    num += count;
+                    count = 0;
+                    break;
+                }
+                qreal rx = (*num++);
+                qreal ry = (*num++);
+                qreal xAxisRotation = (*num++);
+                qreal largeArcFlag  = (*num++);
+                qreal sweepFlag = (*num++);
+                qreal ex = (*num++) + offsetX;
+                qreal ey = (*num++) + offsetY;
+                count -= 7;
+                qreal curx = x;
+                qreal cury = y;
+                pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
+                        int(sweepFlag), ex, ey, curx, cury);
+
+                x = ex;
+                y = ey;
+            }
+                break;
+            case 'A': {
+                if (count < 7) {
+                    num += count;
+                    count = 0;
+                    break;
+                }
+                qreal rx = (*num++);
+                qreal ry = (*num++);
+                qreal xAxisRotation = (*num++);
+                qreal largeArcFlag  = (*num++);
+                qreal sweepFlag = (*num++);
+                qreal ex = (*num++);
+                qreal ey = (*num++);
+                count -= 7;
+                qreal curx = x;
+                qreal cury = y;
+                pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
+                        int(sweepFlag), ex, ey, curx, cury);
+
+                x = ex;
+                y = ey;
+            }
+                break;
+            default:
+                return false;
+            }
+            lastMode = pathElem.toLatin1();
+        }
+    }
+    return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/util/qdeclarativesvgparser_p.h b/src/declarative/util/qdeclarativesvgparser_p.h
new file mode 100644 (file)
index 0000000..0d7be10
--- /dev/null
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclaractive module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECLARATIVESVGPARSER_P_H
+#define QDECLARATIVESVGPARSER_P_H
+
+#include <QtCore/qstring.h>
+#include <QtGui/qpainterpath.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QDeclarativeSvgParser
+{
+    bool parsePathDataFast(const QString &dataStr, QPainterPath &path);
+    void pathArc(QPainterPath &path, qreal rx, qreal ry, qreal x_axis_rotation,
+                 int large_arc_flag, int sweep_flag, qreal x, qreal y, qreal curx,
+                 qreal cury);
+}
+
+QT_END_NAMESPACE
+
+#endif // QDECLARATIVESVGPARSER_P_H
index 965acf8..ddd7026 100644 (file)
@@ -29,7 +29,9 @@ SOURCES += \
     $$PWD/qdeclarativelistmodelworkeragent.cpp \
     $$PWD/qdeclarativepath.cpp \
     $$PWD/qdeclarativechangeset.cpp \
-    $$PWD/qlistmodelinterface.cpp
+    $$PWD/qlistmodelinterface.cpp \
+    $$PWD/qdeclarativepathinterpolator.cpp \
+    $$PWD/qdeclarativesvgparser.cpp
 
 HEADERS += \
     $$PWD/qdeclarativeapplication_p.h \
@@ -65,7 +67,9 @@ HEADERS += \
     $$PWD/qdeclarativepath_p.h \
     $$PWD/qdeclarativepath_p_p.h \
     $$PWD/qdeclarativechangeset_p.h \
-    $$PWD/qlistmodelinterface_p.h
+    $$PWD/qlistmodelinterface_p.h \
+    $$PWD/qdeclarativepathinterpolator_p.h \
+    $$PWD/qdeclarativesvgparser_p.h
 
 contains(QT_CONFIG, xmlpatterns) {
     QT+=xmlpatterns
diff --git a/tests/auto/declarative/qdeclarativeanimations/data/pathAnimation.qml b/tests/auto/declarative/qdeclarativeanimations/data/pathAnimation.qml
new file mode 100644 (file)
index 0000000..d2006a1
--- /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 {
+            startX: 50; startY: 50
+            PathCubic {
+                x: 300; y: 300
+
+                control1X: 300; control1Y: 50
+                control2X: 50; control2Y: 300
+            }
+        }
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeanimations/data/pathInterpolator.qml b/tests/auto/declarative/qdeclarativeanimations/data/pathInterpolator.qml
new file mode 100644 (file)
index 0000000..0104412
--- /dev/null
@@ -0,0 +1,13 @@
+import QtQuick 2.0
+
+PathInterpolator {
+    path: Path {
+        startX: 50; startY: 50
+        PathCubic {
+            x: 300; y: 300
+
+            control1X: 300; control1Y: 50
+            control2X: 50; control2Y: 300
+        }
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeanimations/data/pathTransition.qml b/tests/auto/declarative/qdeclarativeanimations/data/pathTransition.qml
new file mode 100644 (file)
index 0000000..55ffc33
--- /dev/null
@@ -0,0 +1,41 @@
+import QtQuick 2.0
+
+Rectangle {
+    width: 800
+    height: 800
+
+    Rectangle {
+        id: redRect; objectName: "redRect"
+        color: "red"
+        width: 50; height: 50
+        x: 500; y: 50
+    }
+
+    states: State {
+        name: "moved"
+        PropertyChanges {
+            target: redRect
+            x: 100; y: 700
+        }
+    }
+
+    transitions: Transition {
+        to: "moved"; reversible: true
+        PathAnimation {
+            id: pathAnim
+            target: redRect
+            duration: 300
+            path: Path {
+                PathCurve { x: 100; y: 100 }
+                PathCurve { x: 200; y: 350 }
+                PathCurve { x: 600; y: 500 }
+                PathCurve {}
+            }
+        }
+    }
+
+    MouseArea {
+        anchors.fill: parent
+        onClicked: parent.state = parent.state == "moved" ? "" : "moved"
+    }
+}
index 367de01..df840e9 100644 (file)
@@ -44,6 +44,8 @@
 #include <QtDeclarative/qsgview.h>
 #include <QtDeclarative/private/qsgrectangle_p.h>
 #include <QtDeclarative/private/qdeclarativeanimation_p.h>
+#include <QtDeclarative/private/qsganimation_p.h>
+#include <QtDeclarative/private/qdeclarativepathinterpolator_p.h>
 #include <QtDeclarative/private/qsgitem_p.h>
 #include <QVariantAnimation>
 #include <QEasingCurve>
@@ -68,6 +70,8 @@ private slots:
     void simpleNumber();
     void simpleColor();
     void simpleRotation();
+    void simplePath();
+    void pathInterpolator();
     void alwaysRunToEnd();
     void complete();
     void resume();
@@ -77,6 +81,7 @@ private slots:
     void mixedTypes();
     void properties();
     void propertiesTransition();
+    void pathTransition();
     void disabledTransition();
     void invalidDuration();
     void attached();
@@ -213,6 +218,62 @@ void tst_qdeclarativeanimations::simpleRotation()
     QCOMPARE(rect.rotation(), qreal(135));
 }
 
+void tst_qdeclarativeanimations::simplePath()
+{
+    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);
+
+    pathAnim->start();
+    pathAnim->pause();
+
+    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));
+
+    //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));
+}
+
+void tst_qdeclarativeanimations::pathInterpolator()
+{
+    QDeclarativeEngine engine;
+    QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/pathInterpolator.qml"));
+    QDeclarativePathInterpolator *interpolator = qobject_cast<QDeclarativePathInterpolator*>(c.create());
+    QVERIFY(interpolator);
+
+    QCOMPARE(interpolator->progress(), qreal(0));
+    QCOMPARE(interpolator->x(), qreal(50));
+    QCOMPARE(interpolator->y(), qreal(50));
+    QCOMPARE(interpolator->angle(), qreal(0));
+
+    interpolator->setProgress(.5);
+    QCOMPARE(interpolator->progress(), qreal(.5));
+    QCOMPARE(interpolator->x(), qreal(175));
+    QCOMPARE(interpolator->y(), qreal(175));
+    QCOMPARE(interpolator->angle(), qreal(270));
+
+    interpolator->setProgress(1);
+    QCOMPARE(interpolator->progress(), qreal(1));
+    QCOMPARE(interpolator->x(), qreal(300));
+    QCOMPARE(interpolator->y(), qreal(300));
+    QCOMPARE(interpolator->angle(), qreal(0));
+}
+
 void tst_qdeclarativeanimations::alwaysRunToEnd()
 {
     QSGRectangle rect;
@@ -577,6 +638,26 @@ void tst_qdeclarativeanimations::propertiesTransition()
 
 }
 
+void tst_qdeclarativeanimations::pathTransition()
+{
+    QDeclarativeEngine engine;
+    QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/pathTransition.qml"));
+    QSGRectangle *rect = qobject_cast<QSGRectangle*>(c.create());
+    QVERIFY(rect);
+
+    QSGRectangle *myRect = rect->findChild<QSGRectangle*>("redRect");
+    QVERIFY(myRect);
+
+    QSGItemPrivate::get(rect)->setState("moved");
+    QTRY_VERIFY(myRect->x() < 500 && myRect->x() > 100 && myRect->y() > 50 && myRect->y() < 700 );  //animation started
+    QTRY_VERIFY(qFuzzyCompare(myRect->x(), qreal(100)) && qFuzzyCompare(myRect->y(), qreal(700)));
+    QTest::qWait(100);
+
+    QSGItemPrivate::get(rect)->setState("");
+    QTRY_VERIFY(myRect->x() < 500 && myRect->x() > 100 && myRect->y() > 50 && myRect->y() < 700 );  //animation started
+    QTRY_VERIFY(qFuzzyCompare(myRect->x(), qreal(500)) && qFuzzyCompare(myRect->y(), qreal(50)));
+}
+
 void tst_qdeclarativeanimations::disabledTransition()
 {
     QDeclarativeEngine engine;
diff --git a/tests/auto/declarative/qdeclarativepath/data/arc.qml b/tests/auto/declarative/qdeclarativepath/data/arc.qml
new file mode 100644 (file)
index 0000000..000221c
--- /dev/null
@@ -0,0 +1,11 @@
+import QtQuick 2.0
+
+Path {
+    startX: 0; startY: 0
+
+    PathArc {
+        x: 100; y: 100
+        radiusX: 100; radiusY: 100
+        direction: PathArc.Clockwise
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativepath/data/curve.qml b/tests/auto/declarative/qdeclarativepath/data/curve.qml
new file mode 100644 (file)
index 0000000..c571186
--- /dev/null
@@ -0,0 +1,9 @@
+import QtQuick 2.0
+
+Path {
+    startX: 0; startY: 0
+
+    PathCurve { x: 100; y: 50 }
+    PathCurve { x: 50; y: 100 }
+    PathCurve { x: 100; y: 150 }
+}
diff --git a/tests/auto/declarative/qdeclarativepath/data/svg.qml b/tests/auto/declarative/qdeclarativepath/data/svg.qml
new file mode 100644 (file)
index 0000000..cec0f75
--- /dev/null
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+Path {
+    PathSvg { path: "M200,300 Q400,50 600,300 T1000,300" }
+}
diff --git a/tests/auto/declarative/qdeclarativepath/qdeclarativepath.pro b/tests/auto/declarative/qdeclarativepath/qdeclarativepath.pro
new file mode 100644 (file)
index 0000000..d6fe1cf
--- /dev/null
@@ -0,0 +1,17 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_qdeclarativepath.cpp
+
+symbian: {
+    importFiles.files = data
+    importFiles.path = .
+    DEPLOYMENT += importFiles
+} else {
+    DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+CONFIG += parallel_test
+
+QT += core-private gui-private v8-private declarative-private
diff --git a/tests/auto/declarative/qdeclarativepath/tst_qdeclarativepath.cpp b/tests/auto/declarative/qdeclarativepath/tst_qdeclarativepath.cpp
new file mode 100644 (file)
index 0000000..d8a539e
--- /dev/null
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+#include <QtDeclarative/private/qdeclarativepath_p.h>
+
+#include "../../../shared/util.h"
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+#define SRCDIR "."
+#endif
+
+class tst_QDeclarativePath : public QObject
+{
+    Q_OBJECT
+public:
+    tst_QDeclarativePath() {}
+
+private slots:
+    void arc();
+    void catmullromCurve();
+    void svg();
+};
+
+void tst_QDeclarativePath::arc()
+{
+    QDeclarativeEngine engine;
+    QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/arc.qml"));
+    QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create());
+    QVERIFY(obj != 0);
+
+    QCOMPARE(obj->startX(), 0.);
+    QCOMPARE(obj->startY(), 0.);
+
+    QDeclarativeListReference list(obj, "pathElements");
+    QCOMPARE(list.count(), 1);
+
+    QDeclarativePathArc* arc = qobject_cast<QDeclarativePathArc*>(list.at(0));
+    QVERIFY(arc != 0);
+    QCOMPARE(arc->x(), 100.);
+    QCOMPARE(arc->y(), 100.);
+    QCOMPARE(arc->radiusX(), 100.);
+    QCOMPARE(arc->radiusY(), 100.);
+    QCOMPARE(arc->useLargeArc(), false);
+    QCOMPARE(arc->direction(), QDeclarativePathArc::Clockwise);
+
+    QPainterPath path = obj->path();
+    QVERIFY(path != QPainterPath());
+
+    QPointF pos = obj->pointAt(0);
+    QCOMPARE(pos, QPointF(0,0));
+    pos = obj->pointAt(.25);
+    QCOMPARE(pos.toPoint(), QPoint(39,8));  //fuzzy compare
+    pos = obj->pointAt(.75);
+    QCOMPARE(pos.toPoint(), QPoint(92,61)); //fuzzy compare
+    pos = obj->pointAt(1);
+    QCOMPARE(pos, QPointF(100,100));
+}
+
+void tst_QDeclarativePath::catmullromCurve()
+{
+    QDeclarativeEngine engine;
+    QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/curve.qml"));
+    QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create());
+    QVERIFY(obj != 0);
+
+    QCOMPARE(obj->startX(), 0.);
+    QCOMPARE(obj->startY(), 0.);
+
+    QDeclarativeListReference list(obj, "pathElements");
+    QCOMPARE(list.count(), 3);
+
+    QDeclarativePathCatmullRomCurve* arc = qobject_cast<QDeclarativePathCatmullRomCurve*>(list.at(0));
+//    QVERIFY(arc != 0);
+//    QCOMPARE(arc->x(), 100.);
+//    QCOMPARE(arc->y(), 100.);
+//    QCOMPARE(arc->radiusX(), 100.);
+//    QCOMPARE(arc->radiusY(), 100.);
+//    QCOMPARE(arc->useLargeArc(), false);
+//    QCOMPARE(arc->direction(), QDeclarativePathArc::Clockwise);
+
+    QPainterPath path = obj->path();
+    QVERIFY(path != QPainterPath());
+
+    QPointF pos = obj->pointAt(0);
+    QCOMPARE(pos, QPointF(0,0));
+    pos = obj->pointAt(.25);
+    QCOMPARE(pos.toPoint(), QPoint(63,26));  //fuzzy compare
+    pos = obj->pointAt(.75);
+    QCOMPARE(pos.toPoint(), QPoint(51,105)); //fuzzy compare
+    pos = obj->pointAt(1);
+    QCOMPARE(pos, QPointF(100,150));
+}
+
+void tst_QDeclarativePath::svg()
+{
+    QDeclarativeEngine engine;
+    QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/svg.qml"));
+    QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create());
+    QVERIFY(obj != 0);
+
+    QCOMPARE(obj->startX(), 0.);
+    QCOMPARE(obj->startY(), 0.);
+
+    QDeclarativeListReference list(obj, "pathElements");
+    QCOMPARE(list.count(), 1);
+
+    QDeclarativePathSvg* svg = qobject_cast<QDeclarativePathSvg*>(list.at(0));
+    QVERIFY(svg != 0);
+    QCOMPARE(svg->path(), QLatin1String("M200,300 Q400,50 600,300 T1000,300"));
+
+    QPainterPath path = obj->path();
+    QVERIFY(path != QPainterPath());
+
+    QPointF pos = obj->pointAt(0);
+    QCOMPARE(pos, QPointF(200,300));
+    pos = obj->pointAt(.25);
+    QCOMPARE(pos.toPoint(), QPoint(400,175));  //fuzzy compare
+    pos = obj->pointAt(.75);
+    QCOMPARE(pos.toPoint(), QPoint(800,425)); //fuzzy compare
+    pos = obj->pointAt(1);
+    QCOMPARE(pos, QPointF(1000,300));
+}
+
+
+QTEST_MAIN(tst_QDeclarativePath)
+
+#include "tst_qdeclarativepath.moc"