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