Use enumeration in preference to enum when referring to QML API.
[profile/ivi/qtdeclarative.git] / src / quick / util / qquickpath.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickpath_p.h"
43 #include "qquickpath_p_p.h"
44 #include "qquicksvgparser_p.h"
45
46 #include <QSet>
47 #include <QTime>
48
49 #include <private/qbezier_p.h>
50 #include <QtCore/qmath.h>
51 #include <QtCore/qnumeric.h>
52
53 QT_BEGIN_NAMESPACE
54
55 /*!
56     \qmlclass PathElement QQuickPathElement
57     \inqmlmodule QtQuick 2
58     \ingroup qtquick-animation-paths
59     \brief PathElement is the base path type
60
61     This type is the base for all path types.  It cannot
62     be instantiated.
63
64     \sa Path, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
65 */
66
67 /*!
68     \qmlclass Path QQuickPath
69     \inqmlmodule QtQuick 2
70     \ingroup qtquick-animation-paths
71     \brief Defines a path for use by \l PathView
72
73     A Path is composed of one or more path segments - PathLine, PathQuad,
74     PathCubic, PathArc, PathCurve, PathSvg.
75
76     The spacing of the items along the Path can be adjusted via a
77     PathPercent object.
78
79     PathAttribute allows named attributes with values to be defined
80     along the path.
81
82     \sa PathView, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
83 */
84 QQuickPath::QQuickPath(QObject *parent)
85  : QObject(*(new QQuickPathPrivate), parent)
86 {
87 }
88
89 QQuickPath::~QQuickPath()
90 {
91 }
92
93 /*!
94     \qmlproperty real QtQuick2::Path::startX
95     \qmlproperty real QtQuick2::Path::startY
96     These properties hold the starting position of the path.
97 */
98 qreal QQuickPath::startX() const
99 {
100     Q_D(const QQuickPath);
101     return d->startX.isNull ? 0 : d->startX.value;
102 }
103
104 void QQuickPath::setStartX(qreal x)
105 {
106     Q_D(QQuickPath);
107     if (d->startX.isValid() && qFuzzyCompare(x, d->startX))
108         return;
109     d->startX = x;
110     emit startXChanged();
111     processPath();
112 }
113
114 bool QQuickPath::hasStartX() const
115 {
116     Q_D(const QQuickPath);
117     return d->startX.isValid();
118 }
119
120 qreal QQuickPath::startY() const
121 {
122     Q_D(const QQuickPath);
123     return d->startY.isNull ? 0 : d->startY.value;
124 }
125
126 void QQuickPath::setStartY(qreal y)
127 {
128     Q_D(QQuickPath);
129     if (d->startY.isValid() && qFuzzyCompare(y, d->startY))
130         return;
131     d->startY = y;
132     emit startYChanged();
133     processPath();
134 }
135
136 bool QQuickPath::hasStartY() const
137 {
138     Q_D(const QQuickPath);
139     return d->startY.isValid();
140 }
141
142 /*!
143     \qmlproperty bool QtQuick2::Path::closed
144     This property holds whether the start and end of the path are identical.
145 */
146 bool QQuickPath::isClosed() const
147 {
148     Q_D(const QQuickPath);
149     return d->closed;
150 }
151
152 bool QQuickPath::hasEnd() const
153 {
154     Q_D(const QQuickPath);
155     for (int i = d->_pathElements.count() - 1; i > -1; --i) {
156         if (QQuickCurve *curve = qobject_cast<QQuickCurve *>(d->_pathElements.at(i))) {
157             if ((!curve->hasX() && !curve->hasRelativeX()) || (!curve->hasY() && !curve->hasRelativeY()))
158                 return false;
159             else
160                 return true;
161         }
162     }
163     return hasStartX() && hasStartY();
164 }
165
166 /*!
167     \qmlproperty list<PathElement> QtQuick2::Path::pathElements
168     This property holds the objects composing the path.
169
170     \default
171
172     A path can contain the following path objects:
173     \list
174         \li \l PathLine - a straight line to a given position.
175         \li \l PathQuad - a quadratic Bezier curve to a given position with a control point.
176         \li \l PathCubic - a cubic Bezier curve to a given position with two control points.
177         \li \l PathArc - an arc to a given position with a radius.
178         \li \l PathSvg - a path specified as an SVG path data string.
179         \li \l PathCurve - a point on a Catmull-Rom curve.
180         \li \l PathAttribute - an attribute at a given position in the path.
181         \li \l PathPercent - a way to spread out items along various segments of the path.
182     \endlist
183
184     \snippet qml/pathview/pathattributes.qml 2
185 */
186
187 QQmlListProperty<QQuickPathElement> QQuickPath::pathElements()
188 {
189     Q_D(QQuickPath);
190     return QQmlListProperty<QQuickPathElement>(this, d->_pathElements);
191 }
192
193 void QQuickPath::interpolate(int idx, const QString &name, qreal value)
194 {
195     Q_D(QQuickPath);
196     interpolate(d->_attributePoints, idx, name, value);
197 }
198
199 void QQuickPath::interpolate(QList<AttributePoint> &attributePoints, int idx, const QString &name, qreal value)
200 {
201     if (!idx)
202         return;
203
204     qreal lastValue = 0;
205     qreal lastPercent = 0;
206     int search = idx - 1;
207     while(search >= 0) {
208         const AttributePoint &point = attributePoints.at(search);
209         if (point.values.contains(name)) {
210             lastValue = point.values.value(name);
211             lastPercent = point.origpercent;
212             break;
213         }
214         --search;
215     }
216
217     ++search;
218
219     const AttributePoint &curPoint = attributePoints.at(idx);
220
221     for (int ii = search; ii < idx; ++ii) {
222         AttributePoint &point = attributePoints[ii];
223
224         qreal val = lastValue + (value - lastValue) * (point.origpercent - lastPercent) / (curPoint.origpercent - lastPercent);
225         point.values.insert(name, val);
226     }
227 }
228
229 void QQuickPath::endpoint(const QString &name)
230 {
231     Q_D(QQuickPath);
232     const AttributePoint &first = d->_attributePoints.first();
233     qreal val = first.values.value(name);
234     for (int ii = d->_attributePoints.count() - 1; ii >= 0; ii--) {
235         const AttributePoint &point = d->_attributePoints.at(ii);
236         if (point.values.contains(name)) {
237             for (int jj = ii + 1; jj < d->_attributePoints.count(); ++jj) {
238                 AttributePoint &setPoint = d->_attributePoints[jj];
239                 setPoint.values.insert(name, val);
240             }
241             return;
242         }
243     }
244 }
245
246 void QQuickPath::endpoint(QList<AttributePoint> &attributePoints, const QString &name)
247 {
248     const AttributePoint &first = attributePoints.first();
249     qreal val = first.values.value(name);
250     for (int ii = attributePoints.count() - 1; ii >= 0; ii--) {
251         const AttributePoint &point = attributePoints.at(ii);
252         if (point.values.contains(name)) {
253             for (int jj = ii + 1; jj < attributePoints.count(); ++jj) {
254                 AttributePoint &setPoint = attributePoints[jj];
255                 setPoint.values.insert(name, val);
256             }
257             return;
258         }
259     }
260 }
261
262 static QString percentString(QLatin1String("_qfx_percent"));
263
264 void QQuickPath::processPath()
265 {
266     Q_D(QQuickPath);
267
268     if (!d->componentComplete)
269         return;
270
271     d->_pointCache.clear();
272     d->prevBez.isValid = false;
273
274     d->_path = createPath(QPointF(), QPointF(), d->_attributes, d->pathLength, d->_attributePoints, &d->closed);
275
276     emit changed();
277 }
278
279 QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &endPoint, const QStringList &attributes, qreal &pathLength, QList<AttributePoint> &attributePoints, bool *closed)
280 {
281     Q_D(QQuickPath);
282
283     pathLength = 0;
284     attributePoints.clear();
285
286     if (!d->componentComplete)
287         return QPainterPath();
288
289     QPainterPath path;
290
291     AttributePoint first;
292     for (int ii = 0; ii < attributes.count(); ++ii)
293         first.values[attributes.at(ii)] = 0;
294     attributePoints << first;
295
296     qreal startX = d->startX.isValid() ? d->startX.value : startPoint.x();
297     qreal startY = d->startY.isValid() ? d->startY.value : startPoint.y();
298     path.moveTo(startX, startY);
299
300     bool usesPercent = false;
301     int index = 0;
302     foreach (QQuickPathElement *pathElement, d->_pathElements) {
303         if (QQuickCurve *curve = qobject_cast<QQuickCurve *>(pathElement)) {
304             QQuickPathData data;
305             data.index = index;
306             data.endPoint = endPoint;
307             data.curves = d->_pathCurves;
308             curve->addToPath(path, data);
309             AttributePoint p;
310             p.origpercent = path.length();
311             attributePoints << p;
312             ++index;
313         } else if (QQuickPathAttribute *attribute = qobject_cast<QQuickPathAttribute *>(pathElement)) {
314             AttributePoint &point = attributePoints.last();
315             point.values[attribute->name()] = attribute->value();
316             interpolate(attributePoints, attributePoints.count() - 1, attribute->name(), attribute->value());
317         } else if (QQuickPathPercent *percent = qobject_cast<QQuickPathPercent *>(pathElement)) {
318             AttributePoint &point = attributePoints.last();
319             point.values[percentString] = percent->value();
320             interpolate(attributePoints, attributePoints.count() - 1, percentString, percent->value());
321             usesPercent = true;
322         }
323     }
324
325     // Fixup end points
326     const AttributePoint &last = attributePoints.last();
327     for (int ii = 0; ii < attributes.count(); ++ii) {
328         if (!last.values.contains(attributes.at(ii)))
329             endpoint(attributePoints, attributes.at(ii));
330     }
331     if (usesPercent && !last.values.contains(percentString)) {
332         d->_attributePoints.last().values[percentString] = 1;
333         interpolate(d->_attributePoints.count() - 1, percentString, 1);
334     }
335
336
337     // Adjust percent
338     qreal length = path.length();
339     qreal prevpercent = 0;
340     qreal prevorigpercent = 0;
341     for (int ii = 0; ii < attributePoints.count(); ++ii) {
342         const AttributePoint &point = attributePoints.at(ii);
343         if (point.values.contains(percentString)) { //special string for QQuickPathPercent
344             if ( ii > 0) {
345                 qreal scale = (attributePoints[ii].origpercent/length - prevorigpercent) /
346                             (point.values.value(percentString)-prevpercent);
347                 attributePoints[ii].scale = scale;
348             }
349             attributePoints[ii].origpercent /= length;
350             attributePoints[ii].percent = point.values.value(percentString);
351             prevorigpercent = attributePoints[ii].origpercent;
352             prevpercent = attributePoints[ii].percent;
353         } else {
354             attributePoints[ii].origpercent /= length;
355             attributePoints[ii].percent = attributePoints[ii].origpercent;
356         }
357     }
358
359     if (closed) {
360         QPointF end = path.currentPosition();
361         *closed = length > 0 && startX == end.x() && startY == end.y();
362     }
363     pathLength = length;
364
365     return path;
366 }
367
368 void QQuickPath::classBegin()
369 {
370     Q_D(QQuickPath);
371     d->componentComplete = false;
372 }
373
374 void QQuickPath::componentComplete()
375 {
376     Q_D(QQuickPath);
377     QSet<QString> attrs;
378     d->componentComplete = true;
379
380     // First gather up all the attributes
381     foreach (QQuickPathElement *pathElement, d->_pathElements) {
382         if (QQuickCurve *curve =
383             qobject_cast<QQuickCurve *>(pathElement))
384             d->_pathCurves.append(curve);
385         else if (QQuickPathAttribute *attribute =
386                  qobject_cast<QQuickPathAttribute *>(pathElement))
387             attrs.insert(attribute->name());
388     }
389     d->_attributes = attrs.toList();
390
391     processPath();
392
393     foreach (QQuickPathElement *pathElement, d->_pathElements)
394         connect(pathElement, SIGNAL(changed()), this, SLOT(processPath()));
395 }
396
397 QPainterPath QQuickPath::path() const
398 {
399     Q_D(const QQuickPath);
400     return d->_path;
401 }
402
403 QStringList QQuickPath::attributes() const
404 {
405     Q_D(const QQuickPath);
406     if (!d->componentComplete) {
407         QSet<QString> attrs;
408
409         // First gather up all the attributes
410         foreach (QQuickPathElement *pathElement, d->_pathElements) {
411             if (QQuickPathAttribute *attribute =
412                 qobject_cast<QQuickPathAttribute *>(pathElement))
413                 attrs.insert(attribute->name());
414         }
415         return attrs.toList();
416     }
417     return d->_attributes;
418 }
419
420 static inline QBezier nextBezier(const QPainterPath &path, int *current, qreal *bezLength, bool reverse = false)
421 {
422     const int lastElement = reverse ? 0 : path.elementCount() - 1;
423     const int start = reverse ? *current - 1 : *current + 1;
424     for (int i=start; reverse ? i >= lastElement : i <= lastElement; reverse ? --i : ++i) {
425         const QPainterPath::Element &e = path.elementAt(i);
426
427         switch (e.type) {
428         case QPainterPath::MoveToElement:
429             break;
430         case QPainterPath::LineToElement:
431         {
432             QLineF line(path.elementAt(i-1), e);
433             *bezLength = line.length();
434             QPointF a = path.elementAt(i-1);
435             QPointF delta = e - a;
436             *current = i;
437             return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
438         }
439         case QPainterPath::CurveToElement:
440         {
441             QBezier b = QBezier::fromPoints(path.elementAt(i-1),
442                                             e,
443                                             path.elementAt(i+1),
444                                             path.elementAt(i+2));
445             *bezLength = b.length();
446             *current = i;
447             return b;
448         }
449         default:
450             break;
451         }
452     }
453     *current = lastElement;
454     *bezLength = 0;
455     return QBezier();
456 }
457
458 static inline int segmentCount(const QPainterPath &path, qreal pathLength)
459 {
460     // In the really simple case of a single straight line we can interpolate without jitter
461     // between just two points.
462     if (path.elementCount() == 2
463             && path.elementAt(0).type == QPainterPath::MoveToElement
464             && path.elementAt(1).type == QPainterPath::LineToElement) {
465         return 1;
466     }
467     // more points means less jitter between items as they move along the
468     // path, but takes longer to generate
469     return qCeil(pathLength*5);
470 }
471
472 //derivative of the equation
473 static inline qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d)
474 {
475     return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
476 }
477
478 void QQuickPath::createPointCache() const
479 {
480     Q_D(const QQuickPath);
481     qreal pathLength = d->pathLength;
482     if (pathLength <= 0 || qIsNaN(pathLength))
483         return;
484
485     const int segments = segmentCount(d->_path, pathLength);
486     const int lastElement = d->_path.elementCount() - 1;
487     d->_pointCache.resize(segments+1);
488
489     int currElement = -1;
490     qreal bezLength = 0;
491     QBezier currBez = nextBezier(d->_path, &currElement, &bezLength);
492     qreal currLength = bezLength;
493     qreal epc = currLength / pathLength;
494
495     for (int i = 0; i < d->_pointCache.size(); i++) {
496         //find which set we are in
497         qreal prevPercent = 0;
498         qreal prevOrigPercent = 0;
499         for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
500             qreal percent = qreal(i)/segments;
501             const AttributePoint &point = d->_attributePoints.at(ii);
502             if (percent < point.percent || ii == d->_attributePoints.count() - 1) { //### || is special case for very last item
503                 qreal elementPercent = (percent - prevPercent);
504
505                 qreal spc = prevOrigPercent + elementPercent * point.scale;
506
507                 while (spc > epc) {
508                     if (currElement > lastElement)
509                         break;
510                     currBez = nextBezier(d->_path, &currElement, &bezLength);
511                     if (bezLength == 0.0) {
512                         currLength = pathLength;
513                         epc = 1.0;
514                         break;
515                     }
516                     currLength += bezLength;
517                     epc = currLength / pathLength;
518                 }
519                 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
520                 d->_pointCache[i] = currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
521                 break;
522             }
523             prevOrigPercent = point.origpercent;
524             prevPercent = point.percent;
525         }
526     }
527 }
528
529 void QQuickPath::invalidateSequentialHistory() const
530 {
531     Q_D(const QQuickPath);
532     d->prevBez.isValid = false;
533 }
534
535 QPointF QQuickPath::sequentialPointAt(qreal p, qreal *angle) const
536 {
537     Q_D(const QQuickPath);
538     return sequentialPointAt(d->_path, d->pathLength, d->_attributePoints, d->prevBez, p, angle);
539 }
540
541 QPointF QQuickPath::sequentialPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
542 {
543     Q_ASSERT(p >= 0.0 && p <= 1.0);
544
545     if (!prevBez.isValid)
546         return p > .5 ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
547                         forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
548
549     return p < prevBez.p ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
550                            forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
551 }
552
553 QPointF QQuickPath::forwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
554 {
555     if (pathLength <= 0 || qIsNaN(pathLength))
556         return path.pointAtPercent(0);  //expensive?
557
558     const int lastElement = path.elementCount() - 1;
559     bool haveCachedBez = prevBez.isValid;
560     int currElement = haveCachedBez ? prevBez.element : -1;
561     qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
562     QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength);
563     qreal currLength = haveCachedBez ? prevBez.currLength : bezLength;
564     qreal epc = currLength / pathLength;
565
566     //find which set we are in
567     qreal prevPercent = 0;
568     qreal prevOrigPercent = 0;
569     for (int ii = 0; ii < attributePoints.count(); ++ii) {
570         qreal percent = p;
571         const AttributePoint &point = attributePoints.at(ii);
572         if (percent < point.percent || ii == attributePoints.count() - 1) {
573             qreal elementPercent = (percent - prevPercent);
574
575             qreal spc = prevOrigPercent + elementPercent * point.scale;
576
577             while (spc > epc) {
578                 Q_ASSERT(!(currElement > lastElement));
579                 Q_UNUSED(lastElement);
580                 currBez = nextBezier(path, &currElement, &bezLength);
581                 currLength += bezLength;
582                 epc = currLength / pathLength;
583             }
584             prevBez.element = currElement;
585             prevBez.bezLength = bezLength;
586             prevBez.currLength = currLength;
587             prevBez.bezier = currBez;
588             prevBez.p = p;
589             prevBez.isValid = true;
590
591             qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
592
593             if (angle) {
594                 qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
595                 qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
596                 *angle = QLineF(0, 0, m1, m2).angle();
597             }
598
599             return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
600         }
601         prevOrigPercent = point.origpercent;
602         prevPercent = point.percent;
603     }
604
605     return QPointF(0,0);
606 }
607
608 //ideally this should be merged with forwardsPointAt
609 QPointF QQuickPath::backwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
610 {
611     if (pathLength <= 0 || qIsNaN(pathLength))
612         return path.pointAtPercent(0);
613
614     const int firstElement = 1; //element 0 is always a MoveTo, which we ignore
615     bool haveCachedBez = prevBez.isValid;
616     int currElement = haveCachedBez ? prevBez.element : path.elementCount();
617     qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
618     QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength, true /*reverse*/);
619     qreal currLength = haveCachedBez ? prevBez.currLength : pathLength;
620     qreal prevLength = currLength - bezLength;
621     qreal epc = prevLength / pathLength;
622
623     for (int ii = attributePoints.count() - 1; ii > 0; --ii) {
624         qreal percent = p;
625         const AttributePoint &point = attributePoints.at(ii);
626         const AttributePoint &prevPoint = attributePoints.at(ii-1);
627         if (percent > prevPoint.percent || ii == 1) {
628             qreal elementPercent = (percent - prevPoint.percent);
629
630             qreal spc = prevPoint.origpercent + elementPercent * point.scale;
631
632             while (spc < epc) {
633                 Q_ASSERT(!(currElement < firstElement));
634                 Q_UNUSED(firstElement);
635                 currBez = nextBezier(path, &currElement, &bezLength, true /*reverse*/);
636                 //special case for first element is to avoid floating point math
637                 //causing an epc that never hits 0.
638                 currLength = (currElement == firstElement) ? bezLength : prevLength;
639                 prevLength = currLength - bezLength;
640                 epc = prevLength / pathLength;
641             }
642             prevBez.element = currElement;
643             prevBez.bezLength = bezLength;
644             prevBez.currLength = currLength;
645             prevBez.bezier = currBez;
646             prevBez.p = p;
647             prevBez.isValid = true;
648
649             qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
650
651             if (angle) {
652                 qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
653                 qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
654                 *angle = QLineF(0, 0, m1, m2).angle();
655             }
656
657             return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
658         }
659     }
660
661     return QPointF(0,0);
662 }
663
664 QPointF QQuickPath::pointAt(qreal p) const
665 {
666     Q_D(const QQuickPath);
667     if (d->_pointCache.isEmpty()) {
668         createPointCache();
669         if (d->_pointCache.isEmpty())
670             return QPointF();
671     }
672
673     const int segmentCount = d->_pointCache.size() - 1;
674     qreal idxf = p*segmentCount;
675     int idx1 = qFloor(idxf);
676     qreal delta = idxf - idx1;
677     if (idx1 > segmentCount)
678         idx1 = segmentCount;
679     else if (idx1 < 0)
680         idx1 = 0;
681
682     if (delta == 0.0)
683         return d->_pointCache.at(idx1);
684
685     // interpolate between the two points.
686     int idx2 = qCeil(idxf);
687     if (idx2 > segmentCount)
688         idx2 = segmentCount;
689     else if (idx2 < 0)
690         idx2 = 0;
691
692     QPointF p1 = d->_pointCache.at(idx1);
693     QPointF p2 = d->_pointCache.at(idx2);
694     QPointF pos = p1 * (1.0-delta) + p2 * delta;
695
696     return pos;
697 }
698
699 qreal QQuickPath::attributeAt(const QString &name, qreal percent) const
700 {
701     Q_D(const QQuickPath);
702     if (percent < 0 || percent > 1)
703         return 0;
704
705     for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
706         const AttributePoint &point = d->_attributePoints.at(ii);
707
708         if (point.percent == percent) {
709             return point.values.value(name);
710         } else if (point.percent > percent) {
711             qreal lastValue =
712                 ii?(d->_attributePoints.at(ii - 1).values.value(name)):0;
713             qreal lastPercent =
714                 ii?(d->_attributePoints.at(ii - 1).percent):0;
715             qreal curValue = point.values.value(name);
716             qreal curPercent = point.percent;
717
718             return lastValue + (curValue - lastValue) * (percent - lastPercent) / (curPercent - lastPercent);
719         }
720     }
721
722     return 0;
723 }
724
725 /****************************************************************************/
726
727 qreal QQuickCurve::x() const
728 {
729     return _x.isNull ? 0 : _x.value;
730 }
731
732 void QQuickCurve::setX(qreal x)
733 {
734     if (_x.isNull || _x != x) {
735         _x = x;
736         emit xChanged();
737         emit changed();
738     }
739 }
740
741 bool QQuickCurve::hasX()
742 {
743     return _x.isValid();
744 }
745
746 qreal QQuickCurve::y() const
747 {
748     return _y.isNull ? 0 : _y.value;
749 }
750
751 void QQuickCurve::setY(qreal y)
752 {
753     if (_y.isNull || _y != y) {
754         _y = y;
755         emit yChanged();
756         emit changed();
757     }
758 }
759
760 bool QQuickCurve::hasY()
761 {
762     return _y.isValid();
763 }
764
765 qreal QQuickCurve::relativeX() const
766 {
767     return _relativeX;
768 }
769
770 void QQuickCurve::setRelativeX(qreal x)
771 {
772     if (_relativeX.isNull || _relativeX != x) {
773         _relativeX = x;
774         emit relativeXChanged();
775         emit changed();
776     }
777 }
778
779 bool QQuickCurve::hasRelativeX()
780 {
781     return _relativeX.isValid();
782 }
783
784 qreal QQuickCurve::relativeY() const
785 {
786     return _relativeY;
787 }
788
789 void QQuickCurve::setRelativeY(qreal y)
790 {
791     if (_relativeY.isNull || _relativeY != y) {
792         _relativeY = y;
793         emit relativeYChanged();
794         emit changed();
795     }
796 }
797
798 bool QQuickCurve::hasRelativeY()
799 {
800     return _relativeY.isValid();
801 }
802
803 /****************************************************************************/
804
805 /*!
806     \qmlclass PathAttribute QQuickPathAttribute
807     \inqmlmodule QtQuick 2
808     \ingroup qtquick-animation-paths
809     \brief Specifies how to set an attribute at a given position in a Path
810
811     The PathAttribute object allows attributes consisting of a name and
812     a value to be specified for various points along a path.  The
813     attributes are exposed to the delegate as
814     \l{qdeclarativeintroduction.html#attached-properties} {Attached Properties}.
815     The value of an attribute at any particular point along the path is interpolated
816     from the PathAttributes bounding that point.
817
818     The example below shows a path with the items scaled to 30% with
819     opacity 50% at the top of the path and scaled 100% with opacity
820     100% at the bottom.  Note the use of the PathView.iconScale and
821     PathView.iconOpacity attached properties to set the scale and opacity
822     of the delegate.
823
824     \table
825     \row
826     \li \image declarative-pathattribute.png
827     \li
828     \snippet qml/pathview/pathattributes.qml 0
829     (see the PathView documentation for the specification of ContactModel.qml
830      used for ContactModel above.)
831     \endtable
832
833
834     \sa Path
835 */
836
837 /*!
838     \qmlproperty string QtQuick2::PathAttribute::name
839     This property holds the name of the attribute to change.
840
841     This attribute will be available to the delegate as PathView.<name>
842
843     Note that using an existing Item property name such as "opacity" as an
844     attribute is allowed.  This is because path attributes add a new
845     \l{qdeclarativeintroduction.html#attached-properties} {Attached Property}
846     which in no way clashes with existing properties.
847 */
848
849 /*!
850     the name of the attribute to change.
851 */
852
853 QString QQuickPathAttribute::name() const
854 {
855     return _name;
856 }
857
858 void QQuickPathAttribute::setName(const QString &name)
859 {
860     if (_name == name)
861         return;
862      _name = name;
863     emit nameChanged();
864 }
865
866 /*!
867    \qmlproperty real QtQuick2::PathAttribute::value
868    This property holds the value for the attribute.
869
870    The value specified can be used to influence the visual appearance
871    of an item along the path. For example, the following Path specifies
872    an attribute named \e itemRotation, which has the value \e 0 at the
873    beginning of the path, and the value 90 at the end of the path.
874
875    \qml
876    Path {
877        startX: 0
878        startY: 0
879        PathAttribute { name: "itemRotation"; value: 0 }
880        PathLine { x: 100; y: 100 }
881        PathAttribute { name: "itemRotation"; value: 90 }
882    }
883    \endqml
884
885    In our delegate, we can then bind the \e rotation property to the
886    \l{qdeclarativeintroduction.html#attached-properties} {Attached Property}
887    \e PathView.itemRotation created for this attribute.
888
889    \qml
890    Rectangle {
891        width: 10; height: 10
892        rotation: PathView.itemRotation
893    }
894    \endqml
895
896    As each item is positioned along the path, it will be rotated accordingly:
897    an item at the beginning of the path with be not be rotated, an item at
898    the end of the path will be rotated 90 degrees, and an item mid-way along
899    the path will be rotated 45 degrees.
900 */
901
902 /*!
903     the new value of the attribute.
904 */
905 qreal QQuickPathAttribute::value() const
906 {
907     return _value;
908 }
909
910 void QQuickPathAttribute::setValue(qreal value)
911 {
912     if (_value != value) {
913         _value = value;
914         emit valueChanged();
915         emit changed();
916     }
917 }
918
919 /****************************************************************************/
920
921 /*!
922     \qmlclass PathLine QQuickPathLine
923     \inqmlmodule QtQuick 2
924     \ingroup qtquick-animation-paths
925     \brief Defines a straight line
926
927     The example below creates a path consisting of a straight line from
928     0,100 to 200,100:
929
930     \qml
931     Path {
932         startX: 0; startY: 100
933         PathLine { x: 200; y: 100 }
934     }
935     \endqml
936
937     \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
938 */
939
940 /*!
941     \qmlproperty real QtQuick2::PathLine::x
942     \qmlproperty real QtQuick2::PathLine::y
943
944     Defines the end point of the line.
945
946     \sa relativeX, relativeY
947 */
948
949 /*!
950     \qmlproperty real QtQuick2::PathLine::relativeX
951     \qmlproperty real QtQuick2::PathLine::relativeY
952
953     Defines the end point of the line relative to its start.
954
955     If both a relative and absolute end position are specified for a single axis, the relative
956     position will be used.
957
958     Relative and absolute positions can be mixed, for example it is valid to set a relative x
959     and an absolute y.
960
961     \sa x, y
962 */
963
964 inline QPointF positionForCurve(const QQuickPathData &data, const QPointF &prevPoint)
965 {
966     QQuickCurve *curve = data.curves.at(data.index);
967     bool isEnd = data.index == data.curves.size() - 1;
968     return QPointF(curve->hasRelativeX() ? prevPoint.x() + curve->relativeX() : !isEnd || curve->hasX() ? curve->x() : data.endPoint.x(),
969                    curve->hasRelativeY() ? prevPoint.y() + curve->relativeY() : !isEnd || curve->hasY() ? curve->y() : data.endPoint.y());
970 }
971
972 void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data)
973 {
974     path.lineTo(positionForCurve(data, path.currentPosition()));
975 }
976
977 /****************************************************************************/
978
979 /*!
980     \qmlclass PathQuad QQuickPathQuad
981     \inqmlmodule QtQuick 2
982     \ingroup qtquick-animation-paths
983     \brief Defines a quadratic Bezier curve with a control point
984
985     The following QML produces the path shown below:
986     \table
987     \row
988     \li \image declarative-pathquad.png
989     \li
990     \qml
991     Path {
992         startX: 0; startY: 0
993         PathQuad { x: 200; y: 0; controlX: 100; controlY: 150 }
994     }
995     \endqml
996     \endtable
997
998     \sa Path, PathCubic, PathLine, PathArc, PathCurve, PathSvg
999 */
1000
1001 /*!
1002     \qmlproperty real QtQuick2::PathQuad::x
1003     \qmlproperty real QtQuick2::PathQuad::y
1004
1005     Defines the end point of the curve.
1006
1007     \sa relativeX, relativeY
1008 */
1009
1010 /*!
1011     \qmlproperty real QtQuick2::PathQuad::relativeX
1012     \qmlproperty real QtQuick2::PathQuad::relativeY
1013
1014     Defines the end point of the curve relative to its start.
1015
1016     If both a relative and absolute end position are specified for a single axis, the relative
1017     position will be used.
1018
1019     Relative and absolute positions can be mixed, for example it is valid to set a relative x
1020     and an absolute y.
1021
1022     \sa x, y
1023 */
1024
1025 /*!
1026    \qmlproperty real QtQuick2::PathQuad::controlX
1027    \qmlproperty real QtQuick2::PathQuad::controlY
1028
1029    Defines the position of the control point.
1030 */
1031
1032 /*!
1033     the x position of the control point.
1034 */
1035 qreal QQuickPathQuad::controlX() const
1036 {
1037     return _controlX;
1038 }
1039
1040 void QQuickPathQuad::setControlX(qreal x)
1041 {
1042     if (_controlX != x) {
1043         _controlX = x;
1044         emit controlXChanged();
1045         emit changed();
1046     }
1047 }
1048
1049
1050 /*!
1051     the y position of the control point.
1052 */
1053 qreal QQuickPathQuad::controlY() const
1054 {
1055     return _controlY;
1056 }
1057
1058 void QQuickPathQuad::setControlY(qreal y)
1059 {
1060     if (_controlY != y) {
1061         _controlY = y;
1062         emit controlYChanged();
1063         emit changed();
1064     }
1065 }
1066
1067 /*!
1068    \qmlproperty real QtQuick2::PathCubic::relativeControlX
1069    \qmlproperty real QtQuick2::PathCubic::relativeControlY
1070
1071     Defines the position of the control point relative to the curve's start.
1072
1073     If both a relative and absolute control position are specified for a single axis, the relative
1074     position will be used.
1075
1076     Relative and absolute positions can be mixed, for example it is valid to set a relative control x
1077     and an absolute control y.
1078
1079     \sa controlX, controlY
1080 */
1081
1082 qreal QQuickPathQuad::relativeControlX() const
1083 {
1084     return _relativeControlX;
1085 }
1086
1087 void QQuickPathQuad::setRelativeControlX(qreal x)
1088 {
1089     if (_relativeControlX.isNull || _relativeControlX != x) {
1090         _relativeControlX = x;
1091         emit relativeControlXChanged();
1092         emit changed();
1093     }
1094 }
1095
1096 bool QQuickPathQuad::hasRelativeControlX()
1097 {
1098     return _relativeControlX.isValid();
1099 }
1100
1101 qreal QQuickPathQuad::relativeControlY() const
1102 {
1103     return _relativeControlY;
1104 }
1105
1106 void QQuickPathQuad::setRelativeControlY(qreal y)
1107 {
1108     if (_relativeControlY.isNull || _relativeControlY != y) {
1109         _relativeControlY = y;
1110         emit relativeControlYChanged();
1111         emit changed();
1112     }
1113 }
1114
1115 bool QQuickPathQuad::hasRelativeControlY()
1116 {
1117     return _relativeControlY.isValid();
1118 }
1119
1120 void QQuickPathQuad::addToPath(QPainterPath &path, const QQuickPathData &data)
1121 {
1122     const QPointF &prevPoint = path.currentPosition();
1123     QPointF controlPoint(hasRelativeControlX() ? prevPoint.x() + relativeControlX() : controlX(),
1124                          hasRelativeControlY() ? prevPoint.y() + relativeControlY() : controlY());
1125     path.quadTo(controlPoint, positionForCurve(data, path.currentPosition()));
1126 }
1127
1128 /****************************************************************************/
1129
1130 /*!
1131     \qmlclass PathCubic QQuickPathCubic
1132     \inqmlmodule QtQuick 2
1133     \ingroup qtquick-animation-paths
1134     \brief Defines a cubic Bezier curve with two control points
1135
1136     The following QML produces the path shown below:
1137     \table
1138     \row
1139     \li \image declarative-pathcubic.png
1140     \li
1141     \qml
1142     Path {
1143         startX: 20; startY: 0
1144         PathCubic {
1145             x: 180; y: 0
1146             control1X: -10; control1Y: 90
1147             control2X: 210; control2Y: 90
1148         }
1149     }
1150     \endqml
1151     \endtable
1152
1153     \sa Path, PathQuad, PathLine, PathArc, PathCurve, PathSvg
1154 */
1155
1156 /*!
1157     \qmlproperty real QtQuick2::PathCubic::x
1158     \qmlproperty real QtQuick2::PathCubic::y
1159
1160     Defines the end point of the curve.
1161
1162     \sa relativeX, relativeY
1163 */
1164
1165 /*!
1166     \qmlproperty real QtQuick2::PathCubic::relativeX
1167     \qmlproperty real QtQuick2::PathCubic::relativeY
1168
1169     Defines the end point of the curve relative to its start.
1170
1171     If both a relative and absolute end position are specified for a single axis, the relative
1172     position will be used.
1173
1174     Relative and absolute positions can be mixed, for example it is valid to set a relative x
1175     and an absolute y.
1176
1177     \sa x, y
1178 */
1179
1180 /*!
1181    \qmlproperty real QtQuick2::PathCubic::control1X
1182    \qmlproperty real QtQuick2::PathCubic::control1Y
1183
1184     Defines the position of the first control point.
1185 */
1186 qreal QQuickPathCubic::control1X() const
1187 {
1188     return _control1X;
1189 }
1190
1191 void QQuickPathCubic::setControl1X(qreal x)
1192 {
1193     if (_control1X != x) {
1194         _control1X = x;
1195         emit control1XChanged();
1196         emit changed();
1197     }
1198 }
1199
1200 qreal QQuickPathCubic::control1Y() const
1201 {
1202     return _control1Y;
1203 }
1204
1205 void QQuickPathCubic::setControl1Y(qreal y)
1206 {
1207     if (_control1Y != y) {
1208         _control1Y = y;
1209         emit control1YChanged();
1210         emit changed();
1211     }
1212 }
1213
1214 /*!
1215    \qmlproperty real QtQuick2::PathCubic::control2X
1216    \qmlproperty real QtQuick2::PathCubic::control2Y
1217
1218     Defines the position of the second control point.
1219 */
1220 qreal QQuickPathCubic::control2X() const
1221 {
1222     return _control2X;
1223 }
1224
1225 void QQuickPathCubic::setControl2X(qreal x)
1226 {
1227     if (_control2X != x) {
1228         _control2X = x;
1229         emit control2XChanged();
1230         emit changed();
1231     }
1232 }
1233
1234 qreal QQuickPathCubic::control2Y() const
1235 {
1236     return _control2Y;
1237 }
1238
1239 void QQuickPathCubic::setControl2Y(qreal y)
1240 {
1241     if (_control2Y != y) {
1242         _control2Y = y;
1243         emit control2YChanged();
1244         emit changed();
1245     }
1246 }
1247
1248 /*!
1249    \qmlproperty real QtQuick2::PathCubic::relativeControl1X
1250    \qmlproperty real QtQuick2::PathCubic::relativeControl1Y
1251    \qmlproperty real QtQuick2::PathCubic::relativeControl2X
1252    \qmlproperty real QtQuick2::PathCubic::relativeControl2Y
1253
1254     Defines the positions of the control points relative to the curve's start.
1255
1256     If both a relative and absolute control position are specified for a control point's axis, the relative
1257     position will be used.
1258
1259     Relative and absolute positions can be mixed, for example it is valid to set a relative control1 x
1260     and an absolute control1 y.
1261
1262     \sa control1X, control1Y, control2X, control2Y
1263 */
1264
1265 qreal QQuickPathCubic::relativeControl1X() const
1266 {
1267     return _relativeControl1X;
1268 }
1269
1270 void QQuickPathCubic::setRelativeControl1X(qreal x)
1271 {
1272     if (_relativeControl1X.isNull || _relativeControl1X != x) {
1273         _relativeControl1X = x;
1274         emit relativeControl1XChanged();
1275         emit changed();
1276     }
1277 }
1278
1279 bool QQuickPathCubic::hasRelativeControl1X()
1280 {
1281     return _relativeControl1X.isValid();
1282 }
1283
1284 qreal QQuickPathCubic::relativeControl1Y() const
1285 {
1286     return _relativeControl1Y;
1287 }
1288
1289 void QQuickPathCubic::setRelativeControl1Y(qreal y)
1290 {
1291     if (_relativeControl1Y.isNull || _relativeControl1Y != y) {
1292         _relativeControl1Y = y;
1293         emit relativeControl1YChanged();
1294         emit changed();
1295     }
1296 }
1297
1298 bool QQuickPathCubic::hasRelativeControl1Y()
1299 {
1300     return _relativeControl1Y.isValid();
1301 }
1302
1303 qreal QQuickPathCubic::relativeControl2X() const
1304 {
1305     return _relativeControl2X;
1306 }
1307
1308 void QQuickPathCubic::setRelativeControl2X(qreal x)
1309 {
1310     if (_relativeControl2X.isNull || _relativeControl2X != x) {
1311         _relativeControl2X = x;
1312         emit relativeControl2XChanged();
1313         emit changed();
1314     }
1315 }
1316
1317 bool QQuickPathCubic::hasRelativeControl2X()
1318 {
1319     return _relativeControl2X.isValid();
1320 }
1321
1322 qreal QQuickPathCubic::relativeControl2Y() const
1323 {
1324     return _relativeControl2Y;
1325 }
1326
1327 void QQuickPathCubic::setRelativeControl2Y(qreal y)
1328 {
1329     if (_relativeControl2Y.isNull || _relativeControl2Y != y) {
1330         _relativeControl2Y = y;
1331         emit relativeControl2YChanged();
1332         emit changed();
1333     }
1334 }
1335
1336 bool QQuickPathCubic::hasRelativeControl2Y()
1337 {
1338     return _relativeControl2Y.isValid();
1339 }
1340
1341 void QQuickPathCubic::addToPath(QPainterPath &path, const QQuickPathData &data)
1342 {
1343     const QPointF &prevPoint = path.currentPosition();
1344     QPointF controlPoint1(hasRelativeControl1X() ? prevPoint.x() + relativeControl1X() : control1X(),
1345                           hasRelativeControl1Y() ? prevPoint.y() + relativeControl1Y() : control1Y());
1346     QPointF controlPoint2(hasRelativeControl2X() ? prevPoint.x() + relativeControl2X() : control2X(),
1347                           hasRelativeControl2Y() ? prevPoint.y() + relativeControl2Y() : control2Y());
1348     path.cubicTo(controlPoint1, controlPoint2, positionForCurve(data, path.currentPosition()));
1349 }
1350
1351 /****************************************************************************/
1352
1353 /*!
1354     \qmlclass PathCurve QQuickPathCurve
1355     \inqmlmodule QtQuick 2
1356     \ingroup qtquick-animation-paths
1357     \brief Defines a point on a Catmull-Rom curve
1358
1359     PathCurve provides an easy way to specify a curve passing directly through a set of points.
1360     Typically multiple PathCurves are used in a series, as the following example demonstrates:
1361
1362     \snippet qml/path/basiccurve.qml 0
1363
1364     This example produces the following path (with the starting point and PathCurve points
1365     highlighted in red):
1366
1367     \image declarative-pathcurve.png
1368
1369     \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathSvg
1370 */
1371
1372 /*!
1373     \qmlproperty real QtQuick2::PathCurve::x
1374     \qmlproperty real QtQuick2::PathCurve::y
1375
1376     Defines the end point of the curve.
1377
1378     \sa relativeX, relativeY
1379 */
1380
1381 /*!
1382     \qmlproperty real QtQuick2::PathCurve::relativeX
1383     \qmlproperty real QtQuick2::PathCurve::relativeY
1384
1385     Defines the end point of the curve relative to its start.
1386
1387     If both a relative and absolute end position are specified for a single axis, the relative
1388     position will be used.
1389
1390     Relative and absolute positions can be mixed, for example it is valid to set a relative x
1391     and an absolute y.
1392
1393     \sa x, y
1394 */
1395
1396 inline QPointF previousPathPosition(const QPainterPath &path)
1397 {
1398     int count = path.elementCount();
1399     if (count < 1)
1400         return QPointF();
1401
1402     int index = path.elementAt(count-1).type == QPainterPath::CurveToDataElement ? count - 4 : count - 2;
1403     return index > -1 ? QPointF(path.elementAt(index)) : path.pointAtPercent(0);
1404 }
1405
1406 void QQuickPathCatmullRomCurve::addToPath(QPainterPath &path, const QQuickPathData &data)
1407 {
1408     //here we convert catmull-rom spline to bezier for use in QPainterPath.
1409     //basic conversion algorithm:
1410     //  catmull-rom points * inverse bezier matrix * catmull-rom matrix = bezier points
1411     //each point in the catmull-rom spline produces a bezier endpoint + 2 control points
1412     //calculations for each point use a moving window of 4 points
1413     //  (previous 2 points + current point + next point)
1414     QPointF prevFar, prev, point, next;
1415
1416     //get previous points
1417     int index = data.index - 1;
1418     QQuickCurve *curve = index == -1 ? 0 : data.curves.at(index);
1419     if (qobject_cast<QQuickPathCatmullRomCurve*>(curve)) {
1420         prev = path.currentPosition();
1421         prevFar = previousPathPosition(path);
1422     } else {
1423         prev = path.currentPosition();
1424         bool prevFarSet = false;
1425         if (index == -1 && data.curves.count() > 1) {
1426             if (qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(data.curves.count()-1))) {
1427                 //TODO: profile and optimize
1428                 QPointF pos = prev;
1429                 QQuickPathData loopData;
1430                 loopData.endPoint = data.endPoint;
1431                 loopData.curves = data.curves;
1432                 for (int i = data.index; i < data.curves.count(); ++i) {
1433                     loopData.index = i;
1434                     pos = positionForCurve(loopData, pos);
1435                     if (i == data.curves.count()-2)
1436                         prevFar = pos;
1437                 }
1438                 if (pos == QPointF(path.elementAt(0))) {
1439                     //this is a closed path starting and ending with catmull-rom segments.
1440                     //we try to smooth the join point
1441                     prevFarSet = true;
1442                 }
1443             }
1444         }
1445         if (!prevFarSet)
1446             prevFar = prev;
1447     }
1448
1449     //get current point
1450     point = positionForCurve(data, path.currentPosition());
1451
1452     //get next point
1453     index = data.index + 1;
1454     if (index < data.curves.count() && qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(index))) {
1455         QQuickPathData nextData;
1456         nextData.index = index;
1457         nextData.endPoint = data.endPoint;
1458         nextData.curves = data.curves;
1459         next = positionForCurve(nextData, point);
1460     } else {
1461         if (point == QPointF(path.elementAt(0)) && qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(0))) {
1462             //this is a closed path starting and ending with catmull-rom segments.
1463             //we try to smooth the join point
1464             next = QPointF(path.elementAt(3));  //the first catmull-rom point
1465         } else
1466             next = point;
1467     }
1468
1469     /*
1470         full conversion matrix (inverse bezier * catmull-rom):
1471         0.000,  1.000,  0.000,  0.000,
1472         -0.167,  1.000,  0.167,  0.000,
1473         0.000,  0.167,  1.000, -0.167,
1474         0.000,  0.000,  1.000,  0.000
1475
1476         conversion doesn't require full matrix multiplication,
1477         so below we simplify
1478     */
1479     QPointF control1(prevFar.x() * qreal(-0.167) +
1480                      prev.x() +
1481                      point.x() * qreal(0.167),
1482                      prevFar.y() * qreal(-0.167) +
1483                      prev.y() +
1484                      point.y() * qreal(0.167));
1485
1486     QPointF control2(prev.x() * qreal(0.167) +
1487                      point.x() +
1488                      next.x() * qreal(-0.167),
1489                      prev.y() * qreal(0.167) +
1490                      point.y() +
1491                      next.y() * qreal(-0.167));
1492
1493     path.cubicTo(control1, control2, point);
1494 }
1495
1496 /****************************************************************************/
1497
1498 /*!
1499     \qmlclass PathArc QQuickPathArc
1500     \inqmlmodule QtQuick 2
1501     \ingroup qtquick-animation-paths
1502     \brief Defines an arc with the given radius
1503
1504     PathArc provides a simple way of specifying an arc that ends at a given position
1505     and uses the specified radius. It is modeled after the SVG elliptical arc command.
1506
1507     The following QML produces the path shown below:
1508     \table
1509     \row
1510     \li \image declarative-patharc.png
1511     \li \snippet qml/path/basicarc.qml 0
1512     \endtable
1513
1514     Note that a single PathArc cannot be used to specify a circle. Instead, you can
1515     use two PathArc elements, each specifying half of the circle.
1516
1517     \sa Path, PathLine, PathQuad, PathCubic, PathCurve, PathSvg
1518 */
1519
1520 /*!
1521     \qmlproperty real QtQuick2::PathArc::x
1522     \qmlproperty real QtQuick2::PathArc::y
1523
1524     Defines the end point of the arc.
1525
1526     \sa relativeX, relativeY
1527 */
1528
1529 /*!
1530     \qmlproperty real QtQuick2::PathArc::relativeX
1531     \qmlproperty real QtQuick2::PathArc::relativeY
1532
1533     Defines the end point of the arc relative to its start.
1534
1535     If both a relative and absolute end position are specified for a single axis, the relative
1536     position will be used.
1537
1538     Relative and absolute positions can be mixed, for example it is valid to set a relative x
1539     and an absolute y.
1540
1541     \sa x, y
1542 */
1543
1544 /*!
1545     \qmlproperty real QtQuick2::PathArc::radiusX
1546     \qmlproperty real QtQuick2::PathArc::radiusY
1547
1548     Defines the radius of the arc.
1549
1550     The following QML demonstrates how different radius values can be used to change
1551     the shape of the arc:
1552     \table
1553     \row
1554     \li \image declarative-arcradius.png
1555     \li \snippet qml/path/arcradius.qml 0
1556     \endtable
1557 */
1558
1559 qreal QQuickPathArc::radiusX() const
1560 {
1561     return _radiusX;
1562 }
1563
1564 void QQuickPathArc::setRadiusX(qreal radius)
1565 {
1566     if (_radiusX == radius)
1567         return;
1568
1569     _radiusX = radius;
1570     emit radiusXChanged();
1571 }
1572
1573 qreal QQuickPathArc::radiusY() const
1574 {
1575     return _radiusY;
1576 }
1577
1578 void QQuickPathArc::setRadiusY(qreal radius)
1579 {
1580     if (_radiusY == radius)
1581         return;
1582
1583     _radiusY = radius;
1584     emit radiusYChanged();
1585 }
1586
1587 /*!
1588     \qmlproperty bool QtQuick2::PathArc::useLargeArc
1589     Whether to use a large arc as defined by the arc points.
1590
1591     Given fixed start and end positions, radius, and direction,
1592     there are two possible arcs that can fit the data. useLargeArc
1593     is used to distinguish between these. For example, the following
1594     QML can produce either of the two illustrated arcs below by
1595     changing the value of useLargeArc.
1596
1597     \table
1598     \row
1599     \li \image declarative-largearc.png
1600     \li \snippet qml/path/largearc.qml 0
1601     \endtable
1602
1603     The default value is false.
1604 */
1605
1606 bool QQuickPathArc::useLargeArc() const
1607 {
1608     return _useLargeArc;
1609 }
1610
1611 void QQuickPathArc::setUseLargeArc(bool largeArc)
1612 {
1613     if (_useLargeArc == largeArc)
1614         return;
1615
1616     _useLargeArc = largeArc;
1617     emit useLargeArcChanged();
1618 }
1619
1620 /*!
1621     \qmlproperty enumeration QtQuick2::PathArc::direction
1622
1623     Defines the direction of the arc. Possible values are
1624     PathArc.Clockwise (default) and PathArc.Counterclockwise.
1625
1626     The following QML can produce either of the two illustrated arcs below
1627     by changing the value of direction.
1628     \table
1629     \row
1630     \li \image declarative-arcdirection.png
1631     \li \snippet qml/path/arcdirection.qml 0
1632     \endtable
1633
1634     \sa useLargeArc
1635 */
1636
1637 QQuickPathArc::ArcDirection QQuickPathArc::direction() const
1638 {
1639     return _direction;
1640 }
1641
1642 void QQuickPathArc::setDirection(ArcDirection direction)
1643 {
1644     if (_direction == direction)
1645         return;
1646
1647     _direction = direction;
1648     emit directionChanged();
1649 }
1650
1651 void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data)
1652 {
1653     const QPointF &startPoint = path.currentPosition();
1654     const QPointF &endPoint = positionForCurve(data, startPoint);
1655     QQuickSvgParser::pathArc(path,
1656             _radiusX,
1657             _radiusY,
1658             0,  //xAxisRotation
1659             _useLargeArc,
1660             _direction == Clockwise ? 1 : 0,
1661             endPoint.x(),
1662             endPoint.y(),
1663             startPoint.x(), startPoint.y());
1664 }
1665
1666 /****************************************************************************/
1667
1668 /*!
1669     \qmlclass PathSvg QQuickPathSvg
1670     \inqmlmodule QtQuick 2
1671     \ingroup qtquick-animation-paths
1672     \brief Defines a path using an SVG path data string
1673
1674     The following QML produces the path shown below:
1675     \table
1676     \row
1677     \li \image declarative-pathsvg.png
1678     \li
1679     \qml
1680     Path {
1681         startX: 50; startY: 50
1682         PathSvg { path: "L 150 50 L 100 150 z" }
1683     }
1684     \endqml
1685     \endtable
1686
1687     \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathCurve
1688 */
1689
1690 /*!
1691     \qmlproperty string QtQuick2::PathSvg::path
1692
1693     The SVG path data string specifying the path.
1694
1695     See \l {http://www.w3.org/TR/SVG/paths.html#PathData}{W3C SVG Path Data}
1696     for more details on this format.
1697 */
1698
1699 QString QQuickPathSvg::path() const
1700 {
1701     return _path;
1702 }
1703
1704 void QQuickPathSvg::setPath(const QString &path)
1705 {
1706     if (_path == path)
1707         return;
1708
1709     _path = path;
1710     emit pathChanged();
1711 }
1712
1713 void QQuickPathSvg::addToPath(QPainterPath &path, const QQuickPathData &)
1714 {
1715     QQuickSvgParser::parsePathDataFast(_path, path);
1716 }
1717
1718 /****************************************************************************/
1719
1720 /*!
1721     \qmlclass PathPercent QQuickPathPercent
1722     \inqmlmodule QtQuick 2
1723     \ingroup qtquick-animation-paths
1724     \brief Manipulates the way a path is interpreted
1725
1726     PathPercent allows you to manipulate the spacing between items on a
1727     PathView's path. You can use it to bunch together items on part of
1728     the path, and spread them out on other parts of the path.
1729
1730     The examples below show the normal distribution of items along a path
1731     compared to a distribution which places 50% of the items along the
1732     PathLine section of the path.
1733     \table
1734     \row
1735     \li \image declarative-nopercent.png
1736     \li
1737     \qml
1738     PathView {
1739         // ...
1740         Path {
1741             startX: 20; startY: 0
1742             PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
1743             PathLine { x: 150; y: 80 }
1744             PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
1745         }
1746     }
1747     \endqml
1748     \row
1749     \li \image declarative-percent.png
1750     \li
1751     \qml
1752     PathView {
1753         // ...
1754         Path {
1755             startX: 20; startY: 0
1756             PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
1757             PathPercent { value: 0.25 }
1758             PathLine { x: 150; y: 80 }
1759             PathPercent { value: 0.75 }
1760             PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
1761             PathPercent { value: 1 }
1762         }
1763     }
1764     \endqml
1765     \endtable
1766
1767     \sa Path
1768 */
1769
1770 /*!
1771     \qmlproperty real QtQuick2::PathPercent::value
1772     The proportion of items that should be laid out up to this point.
1773
1774     This value should always be higher than the last value specified
1775     by a PathPercent at a previous position in the Path.
1776
1777     In the following example we have a Path made up of three PathLines.
1778     Normally, the items of the PathView would be laid out equally along
1779     this path, with an equal number of items per line segment. PathPercent
1780     allows us to specify that the first and third lines should each hold
1781     10% of the laid out items, while the second line should hold the remaining
1782     80%.
1783
1784     \qml
1785     PathView {
1786         // ...
1787         Path {
1788             startX: 0; startY: 0
1789             PathLine { x:100; y: 0; }
1790             PathPercent { value: 0.1 }
1791             PathLine { x: 100; y: 100 }
1792             PathPercent { value: 0.9 }
1793             PathLine { x: 100; y: 0 }
1794             PathPercent { value: 1 }
1795         }
1796     }
1797     \endqml
1798 */
1799
1800 qreal QQuickPathPercent::value() const
1801 {
1802     return _value;
1803 }
1804
1805 void QQuickPathPercent::setValue(qreal value)
1806 {
1807     if (_value != value) {
1808         _value = value;
1809         emit valueChanged();
1810         emit changed();
1811     }
1812 }
1813 QT_END_NAMESPACE