Merge master into api_changes
[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 qml-view-elements
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 qml-view-elements
71     \brief A Path object 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 doc/src/snippets/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 //derivative of the equation
459 static inline qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d)
460 {
461     return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
462 }
463
464 void QQuickPath::createPointCache() const
465 {
466     Q_D(const QQuickPath);
467     qreal pathLength = d->pathLength;
468     if (pathLength <= 0 || qIsNaN(pathLength))
469         return;
470     // more points means less jitter between items as they move along the
471     // path, but takes longer to generate
472     const int points = qCeil(pathLength*5);
473     const int lastElement = d->_path.elementCount() - 1;
474     d->_pointCache.resize(points+1);
475
476     int currElement = -1;
477     qreal bezLength = 0;
478     QBezier currBez = nextBezier(d->_path, &currElement, &bezLength);
479     qreal currLength = bezLength;
480     qreal epc = currLength / pathLength;
481
482     for (int i = 0; i < d->_pointCache.size(); i++) {
483         //find which set we are in
484         qreal prevPercent = 0;
485         qreal prevOrigPercent = 0;
486         for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
487             qreal percent = qreal(i)/points;
488             const AttributePoint &point = d->_attributePoints.at(ii);
489             if (percent < point.percent || ii == d->_attributePoints.count() - 1) { //### || is special case for very last item
490                 qreal elementPercent = (percent - prevPercent);
491
492                 qreal spc = prevOrigPercent + elementPercent * point.scale;
493
494                 while (spc > epc) {
495                     if (currElement > lastElement)
496                         break;
497                     currBez = nextBezier(d->_path, &currElement, &bezLength);
498                     if (bezLength == 0.0) {
499                         currLength = pathLength;
500                         epc = 1.0;
501                         break;
502                     }
503                     currLength += bezLength;
504                     epc = currLength / pathLength;
505                 }
506                 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
507                 d->_pointCache[i] = currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
508                 break;
509             }
510             prevOrigPercent = point.origpercent;
511             prevPercent = point.percent;
512         }
513     }
514 }
515
516 void QQuickPath::invalidateSequentialHistory() const
517 {
518     Q_D(const QQuickPath);
519     d->prevBez.isValid = false;
520 }
521
522 QPointF QQuickPath::sequentialPointAt(qreal p, qreal *angle) const
523 {
524     Q_D(const QQuickPath);
525     return sequentialPointAt(d->_path, d->pathLength, d->_attributePoints, d->prevBez, p, angle);
526 }
527
528 QPointF QQuickPath::sequentialPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
529 {
530     Q_ASSERT(p >= 0.0 && p <= 1.0);
531
532     if (!prevBez.isValid)
533         return p > .5 ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
534                         forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
535
536     return p < prevBez.p ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
537                            forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
538 }
539
540 QPointF QQuickPath::forwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
541 {
542     if (pathLength <= 0 || qIsNaN(pathLength))
543         return path.pointAtPercent(0);  //expensive?
544
545     const int lastElement = path.elementCount() - 1;
546     bool haveCachedBez = prevBez.isValid;
547     int currElement = haveCachedBez ? prevBez.element : -1;
548     qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
549     QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength);
550     qreal currLength = haveCachedBez ? prevBez.currLength : bezLength;
551     qreal epc = currLength / pathLength;
552
553     //find which set we are in
554     qreal prevPercent = 0;
555     qreal prevOrigPercent = 0;
556     for (int ii = 0; ii < attributePoints.count(); ++ii) {
557         qreal percent = p;
558         const AttributePoint &point = attributePoints.at(ii);
559         if (percent < point.percent || ii == attributePoints.count() - 1) {
560             qreal elementPercent = (percent - prevPercent);
561
562             qreal spc = prevOrigPercent + elementPercent * point.scale;
563
564             while (spc > epc) {
565                 Q_ASSERT(!(currElement > lastElement));
566                 Q_UNUSED(lastElement);
567                 currBez = nextBezier(path, &currElement, &bezLength);
568                 currLength += bezLength;
569                 epc = currLength / pathLength;
570             }
571             prevBez.element = currElement;
572             prevBez.bezLength = bezLength;
573             prevBez.currLength = currLength;
574             prevBez.bezier = currBez;
575             prevBez.p = p;
576             prevBez.isValid = true;
577
578             qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
579
580             if (angle) {
581                 qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
582                 qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
583                 *angle = QLineF(0, 0, m1, m2).angle();
584             }
585
586             return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
587         }
588         prevOrigPercent = point.origpercent;
589         prevPercent = point.percent;
590     }
591
592     return QPointF(0,0);
593 }
594
595 //ideally this should be merged with forwardsPointAt
596 QPointF QQuickPath::backwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
597 {
598     if (pathLength <= 0 || qIsNaN(pathLength))
599         return path.pointAtPercent(0);
600
601     const int firstElement = 1; //element 0 is always a MoveTo, which we ignore
602     bool haveCachedBez = prevBez.isValid;
603     int currElement = haveCachedBez ? prevBez.element : path.elementCount();
604     qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
605     QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength, true /*reverse*/);
606     qreal currLength = haveCachedBez ? prevBez.currLength : pathLength;
607     qreal prevLength = currLength - bezLength;
608     qreal epc = prevLength / pathLength;
609
610     for (int ii = attributePoints.count() - 1; ii > 0; --ii) {
611         qreal percent = p;
612         const AttributePoint &point = attributePoints.at(ii);
613         const AttributePoint &prevPoint = attributePoints.at(ii-1);
614         if (percent > prevPoint.percent || ii == 1) {
615             qreal elementPercent = (percent - prevPoint.percent);
616
617             qreal spc = prevPoint.origpercent + elementPercent * point.scale;
618
619             while (spc < epc) {
620                 Q_ASSERT(!(currElement < firstElement));
621                 Q_UNUSED(firstElement);
622                 currBez = nextBezier(path, &currElement, &bezLength, true /*reverse*/);
623                 //special case for first element is to avoid floating point math
624                 //causing an epc that never hits 0.
625                 currLength = (currElement == firstElement) ? bezLength : prevLength;
626                 prevLength = currLength - bezLength;
627                 epc = prevLength / pathLength;
628             }
629             prevBez.element = currElement;
630             prevBez.bezLength = bezLength;
631             prevBez.currLength = currLength;
632             prevBez.bezier = currBez;
633             prevBez.p = p;
634             prevBez.isValid = true;
635
636             qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
637
638             if (angle) {
639                 qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
640                 qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
641                 *angle = QLineF(0, 0, m1, m2).angle();
642             }
643
644             return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
645         }
646     }
647
648     return QPointF(0,0);
649 }
650
651 QPointF QQuickPath::pointAt(qreal p) const
652 {
653     Q_D(const QQuickPath);
654     if (d->_pointCache.isEmpty()) {
655         createPointCache();
656         if (d->_pointCache.isEmpty())
657             return QPointF();
658     }
659
660     const int pointCacheSize = d->_pointCache.size();
661     qreal idxf = p*pointCacheSize;
662     int idx1 = qFloor(idxf);
663     qreal delta = idxf - idx1;
664     if (idx1 >= pointCacheSize)
665         idx1 = pointCacheSize - 1;
666     else if (idx1 < 0)
667         idx1 = 0;
668
669     if (delta == 0.0)
670         return d->_pointCache.at(idx1);
671
672     // interpolate between the two points.
673     int idx2 = qCeil(idxf);
674     if (idx2 >= pointCacheSize)
675         idx2 = pointCacheSize - 1;
676     else if (idx2 < 0)
677         idx2 = 0;
678
679     QPointF p1 = d->_pointCache.at(idx1);
680     QPointF p2 = d->_pointCache.at(idx2);
681     QPointF pos = p1 * (1.0-delta) + p2 * delta;
682
683     return pos;
684 }
685
686 qreal QQuickPath::attributeAt(const QString &name, qreal percent) const
687 {
688     Q_D(const QQuickPath);
689     if (percent < 0 || percent > 1)
690         return 0;
691
692     for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
693         const AttributePoint &point = d->_attributePoints.at(ii);
694
695         if (point.percent == percent) {
696             return point.values.value(name);
697         } else if (point.percent > percent) {
698             qreal lastValue =
699                 ii?(d->_attributePoints.at(ii - 1).values.value(name)):0;
700             qreal lastPercent =
701                 ii?(d->_attributePoints.at(ii - 1).percent):0;
702             qreal curValue = point.values.value(name);
703             qreal curPercent = point.percent;
704
705             return lastValue + (curValue - lastValue) * (percent - lastPercent) / (curPercent - lastPercent);
706         }
707     }
708
709     return 0;
710 }
711
712 /****************************************************************************/
713
714 qreal QQuickCurve::x() const
715 {
716     return _x.isNull ? 0 : _x.value;
717 }
718
719 void QQuickCurve::setX(qreal x)
720 {
721     if (_x.isNull || _x != x) {
722         _x = x;
723         emit xChanged();
724         emit changed();
725     }
726 }
727
728 bool QQuickCurve::hasX()
729 {
730     return _x.isValid();
731 }
732
733 qreal QQuickCurve::y() const
734 {
735     return _y.isNull ? 0 : _y.value;
736 }
737
738 void QQuickCurve::setY(qreal y)
739 {
740     if (_y.isNull || _y != y) {
741         _y = y;
742         emit yChanged();
743         emit changed();
744     }
745 }
746
747 bool QQuickCurve::hasY()
748 {
749     return _y.isValid();
750 }
751
752 qreal QQuickCurve::relativeX() const
753 {
754     return _relativeX;
755 }
756
757 void QQuickCurve::setRelativeX(qreal x)
758 {
759     if (_relativeX.isNull || _relativeX != x) {
760         _relativeX = x;
761         emit relativeXChanged();
762         emit changed();
763     }
764 }
765
766 bool QQuickCurve::hasRelativeX()
767 {
768     return _relativeX.isValid();
769 }
770
771 qreal QQuickCurve::relativeY() const
772 {
773     return _relativeY;
774 }
775
776 void QQuickCurve::setRelativeY(qreal y)
777 {
778     if (_relativeY.isNull || _relativeY != y) {
779         _relativeY = y;
780         emit relativeYChanged();
781         emit changed();
782     }
783 }
784
785 bool QQuickCurve::hasRelativeY()
786 {
787     return _relativeY.isValid();
788 }
789
790 /****************************************************************************/
791
792 /*!
793     \qmlclass PathAttribute QQuickPathAttribute
794     \inqmlmodule QtQuick 2
795     \ingroup qml-view-elements
796     \brief The PathAttribute allows setting an attribute at a given position in a Path.
797
798     The PathAttribute object allows attributes consisting of a name and
799     a value to be specified for various points along a path.  The
800     attributes are exposed to the delegate as
801     \l{qdeclarativeintroduction.html#attached-properties} {Attached Properties}.
802     The value of an attribute at any particular point along the path is interpolated
803     from the PathAttributes bounding that point.
804
805     The example below shows a path with the items scaled to 30% with
806     opacity 50% at the top of the path and scaled 100% with opacity
807     100% at the bottom.  Note the use of the PathView.iconScale and
808     PathView.iconOpacity attached properties to set the scale and opacity
809     of the delegate.
810
811     \table
812     \row
813     \li \image declarative-pathattribute.png
814     \li
815     \snippet doc/src/snippets/qml/pathview/pathattributes.qml 0
816     (see the PathView documentation for the specification of ContactModel.qml
817      used for ContactModel above.)
818     \endtable
819
820
821     \sa Path
822 */
823
824 /*!
825     \qmlproperty string QtQuick2::PathAttribute::name
826     This property holds the name of the attribute to change.
827
828     This attribute will be available to the delegate as PathView.<name>
829
830     Note that using an existing Item property name such as "opacity" as an
831     attribute is allowed.  This is because path attributes add a new
832     \l{qdeclarativeintroduction.html#attached-properties} {Attached Property}
833     which in no way clashes with existing properties.
834 */
835
836 /*!
837     the name of the attribute to change.
838 */
839
840 QString QQuickPathAttribute::name() const
841 {
842     return _name;
843 }
844
845 void QQuickPathAttribute::setName(const QString &name)
846 {
847     if (_name == name)
848         return;
849      _name = name;
850     emit nameChanged();
851 }
852
853 /*!
854    \qmlproperty real QtQuick2::PathAttribute::value
855    This property holds the value for the attribute.
856
857    The value specified can be used to influence the visual appearance
858    of an item along the path. For example, the following Path specifies
859    an attribute named \e itemRotation, which has the value \e 0 at the
860    beginning of the path, and the value 90 at the end of the path.
861
862    \qml
863    Path {
864        startX: 0
865        startY: 0
866        PathAttribute { name: "itemRotation"; value: 0 }
867        PathLine { x: 100; y: 100 }
868        PathAttribute { name: "itemRotation"; value: 90 }
869    }
870    \endqml
871
872    In our delegate, we can then bind the \e rotation property to the
873    \l{qdeclarativeintroduction.html#attached-properties} {Attached Property}
874    \e PathView.itemRotation created for this attribute.
875
876    \qml
877    Rectangle {
878        width: 10; height: 10
879        rotation: PathView.itemRotation
880    }
881    \endqml
882
883    As each item is positioned along the path, it will be rotated accordingly:
884    an item at the beginning of the path with be not be rotated, an item at
885    the end of the path will be rotated 90 degrees, and an item mid-way along
886    the path will be rotated 45 degrees.
887 */
888
889 /*!
890     the new value of the attribute.
891 */
892 qreal QQuickPathAttribute::value() const
893 {
894     return _value;
895 }
896
897 void QQuickPathAttribute::setValue(qreal value)
898 {
899     if (_value != value) {
900         _value = value;
901         emit valueChanged();
902         emit changed();
903     }
904 }
905
906 /****************************************************************************/
907
908 /*!
909     \qmlclass PathLine QQuickPathLine
910     \inqmlmodule QtQuick 2
911     \ingroup qml-view-elements
912     \brief The PathLine defines a straight line.
913
914     The example below creates a path consisting of a straight line from
915     0,100 to 200,100:
916
917     \qml
918     Path {
919         startX: 0; startY: 100
920         PathLine { x: 200; y: 100 }
921     }
922     \endqml
923
924     \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
925 */
926
927 /*!
928     \qmlproperty real QtQuick2::PathLine::x
929     \qmlproperty real QtQuick2::PathLine::y
930
931     Defines the end point of the line.
932
933     \sa relativeX, relativeY
934 */
935
936 /*!
937     \qmlproperty real QtQuick2::PathLine::relativeX
938     \qmlproperty real QtQuick2::PathLine::relativeY
939
940     Defines the end point of the line relative to its start.
941
942     If both a relative and absolute end position are specified for a single axis, the relative
943     position will be used.
944
945     Relative and absolute positions can be mixed, for example it is valid to set a relative x
946     and an absolute y.
947
948     \sa x, y
949 */
950
951 inline QPointF positionForCurve(const QQuickPathData &data, const QPointF &prevPoint)
952 {
953     QQuickCurve *curve = data.curves.at(data.index);
954     bool isEnd = data.index == data.curves.size() - 1;
955     return QPointF(curve->hasRelativeX() ? prevPoint.x() + curve->relativeX() : !isEnd || curve->hasX() ? curve->x() : data.endPoint.x(),
956                    curve->hasRelativeY() ? prevPoint.y() + curve->relativeY() : !isEnd || curve->hasY() ? curve->y() : data.endPoint.y());
957 }
958
959 void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data)
960 {
961     path.lineTo(positionForCurve(data, path.currentPosition()));
962 }
963
964 /****************************************************************************/
965
966 /*!
967     \qmlclass PathQuad QQuickPathQuad
968     \inqmlmodule QtQuick 2
969     \ingroup qml-view-elements
970     \brief The PathQuad defines a quadratic Bezier curve with a control point.
971
972     The following QML produces the path shown below:
973     \table
974     \row
975     \li \image declarative-pathquad.png
976     \li
977     \qml
978     Path {
979         startX: 0; startY: 0
980         PathQuad { x: 200; y: 0; controlX: 100; controlY: 150 }
981     }
982     \endqml
983     \endtable
984
985     \sa Path, PathCubic, PathLine, PathArc, PathCurve, PathSvg
986 */
987
988 /*!
989     \qmlproperty real QtQuick2::PathQuad::x
990     \qmlproperty real QtQuick2::PathQuad::y
991
992     Defines the end point of the curve.
993
994     \sa relativeX, relativeY
995 */
996
997 /*!
998     \qmlproperty real QtQuick2::PathQuad::relativeX
999     \qmlproperty real QtQuick2::PathQuad::relativeY
1000
1001     Defines the end point of the curve relative to its start.
1002
1003     If both a relative and absolute end position are specified for a single axis, the relative
1004     position will be used.
1005
1006     Relative and absolute positions can be mixed, for example it is valid to set a relative x
1007     and an absolute y.
1008
1009     \sa x, y
1010 */
1011
1012 /*!
1013    \qmlproperty real QtQuick2::PathQuad::controlX
1014    \qmlproperty real QtQuick2::PathQuad::controlY
1015
1016    Defines the position of the control point.
1017 */
1018
1019 /*!
1020     the x position of the control point.
1021 */
1022 qreal QQuickPathQuad::controlX() const
1023 {
1024     return _controlX;
1025 }
1026
1027 void QQuickPathQuad::setControlX(qreal x)
1028 {
1029     if (_controlX != x) {
1030         _controlX = x;
1031         emit controlXChanged();
1032         emit changed();
1033     }
1034 }
1035
1036
1037 /*!
1038     the y position of the control point.
1039 */
1040 qreal QQuickPathQuad::controlY() const
1041 {
1042     return _controlY;
1043 }
1044
1045 void QQuickPathQuad::setControlY(qreal y)
1046 {
1047     if (_controlY != y) {
1048         _controlY = y;
1049         emit controlYChanged();
1050         emit changed();
1051     }
1052 }
1053
1054 /*!
1055    \qmlproperty real QtQuick2::PathCubic::relativeControlX
1056    \qmlproperty real QtQuick2::PathCubic::relativeControlY
1057
1058     Defines the position of the control point relative to the curve's start.
1059
1060     If both a relative and absolute control position are specified for a single axis, the relative
1061     position will be used.
1062
1063     Relative and absolute positions can be mixed, for example it is valid to set a relative control x
1064     and an absolute control y.
1065
1066     \sa controlX, controlY
1067 */
1068
1069 qreal QQuickPathQuad::relativeControlX() const
1070 {
1071     return _relativeControlX;
1072 }
1073
1074 void QQuickPathQuad::setRelativeControlX(qreal x)
1075 {
1076     if (_relativeControlX.isNull || _relativeControlX != x) {
1077         _relativeControlX = x;
1078         emit relativeControlXChanged();
1079         emit changed();
1080     }
1081 }
1082
1083 bool QQuickPathQuad::hasRelativeControlX()
1084 {
1085     return _relativeControlX.isValid();
1086 }
1087
1088 qreal QQuickPathQuad::relativeControlY() const
1089 {
1090     return _relativeControlY;
1091 }
1092
1093 void QQuickPathQuad::setRelativeControlY(qreal y)
1094 {
1095     if (_relativeControlY.isNull || _relativeControlY != y) {
1096         _relativeControlY = y;
1097         emit relativeControlYChanged();
1098         emit changed();
1099     }
1100 }
1101
1102 bool QQuickPathQuad::hasRelativeControlY()
1103 {
1104     return _relativeControlY.isValid();
1105 }
1106
1107 void QQuickPathQuad::addToPath(QPainterPath &path, const QQuickPathData &data)
1108 {
1109     const QPointF &prevPoint = path.currentPosition();
1110     QPointF controlPoint(hasRelativeControlX() ? prevPoint.x() + relativeControlX() : controlX(),
1111                          hasRelativeControlY() ? prevPoint.y() + relativeControlY() : controlY());
1112     path.quadTo(controlPoint, positionForCurve(data, path.currentPosition()));
1113 }
1114
1115 /****************************************************************************/
1116
1117 /*!
1118    \qmlclass PathCubic QQuickPathCubic
1119     \inqmlmodule QtQuick 2
1120     \ingroup qml-view-elements
1121    \brief The PathCubic defines a cubic Bezier curve with two control points.
1122
1123     The following QML produces the path shown below:
1124     \table
1125     \row
1126     \li \image declarative-pathcubic.png
1127     \li
1128     \qml
1129     Path {
1130         startX: 20; startY: 0
1131         PathCubic {
1132             x: 180; y: 0
1133             control1X: -10; control1Y: 90
1134             control2X: 210; control2Y: 90
1135         }
1136     }
1137     \endqml
1138     \endtable
1139
1140     \sa Path, PathQuad, PathLine, PathArc, PathCurve, PathSvg
1141 */
1142
1143 /*!
1144     \qmlproperty real QtQuick2::PathCubic::x
1145     \qmlproperty real QtQuick2::PathCubic::y
1146
1147     Defines the end point of the curve.
1148
1149     \sa relativeX, relativeY
1150 */
1151
1152 /*!
1153     \qmlproperty real QtQuick2::PathCubic::relativeX
1154     \qmlproperty real QtQuick2::PathCubic::relativeY
1155
1156     Defines the end point of the curve relative to its start.
1157
1158     If both a relative and absolute end position are specified for a single axis, the relative
1159     position will be used.
1160
1161     Relative and absolute positions can be mixed, for example it is valid to set a relative x
1162     and an absolute y.
1163
1164     \sa x, y
1165 */
1166
1167 /*!
1168    \qmlproperty real QtQuick2::PathCubic::control1X
1169    \qmlproperty real QtQuick2::PathCubic::control1Y
1170
1171     Defines the position of the first control point.
1172 */
1173 qreal QQuickPathCubic::control1X() const
1174 {
1175     return _control1X;
1176 }
1177
1178 void QQuickPathCubic::setControl1X(qreal x)
1179 {
1180     if (_control1X != x) {
1181         _control1X = x;
1182         emit control1XChanged();
1183         emit changed();
1184     }
1185 }
1186
1187 qreal QQuickPathCubic::control1Y() const
1188 {
1189     return _control1Y;
1190 }
1191
1192 void QQuickPathCubic::setControl1Y(qreal y)
1193 {
1194     if (_control1Y != y) {
1195         _control1Y = y;
1196         emit control1YChanged();
1197         emit changed();
1198     }
1199 }
1200
1201 /*!
1202    \qmlproperty real QtQuick2::PathCubic::control2X
1203    \qmlproperty real QtQuick2::PathCubic::control2Y
1204
1205     Defines the position of the second control point.
1206 */
1207 qreal QQuickPathCubic::control2X() const
1208 {
1209     return _control2X;
1210 }
1211
1212 void QQuickPathCubic::setControl2X(qreal x)
1213 {
1214     if (_control2X != x) {
1215         _control2X = x;
1216         emit control2XChanged();
1217         emit changed();
1218     }
1219 }
1220
1221 qreal QQuickPathCubic::control2Y() const
1222 {
1223     return _control2Y;
1224 }
1225
1226 void QQuickPathCubic::setControl2Y(qreal y)
1227 {
1228     if (_control2Y != y) {
1229         _control2Y = y;
1230         emit control2YChanged();
1231         emit changed();
1232     }
1233 }
1234
1235 /*!
1236    \qmlproperty real QtQuick2::PathCubic::relativeControl1X
1237    \qmlproperty real QtQuick2::PathCubic::relativeControl1Y
1238    \qmlproperty real QtQuick2::PathCubic::relativeControl2X
1239    \qmlproperty real QtQuick2::PathCubic::relativeControl2Y
1240
1241     Defines the positions of the control points relative to the curve's start.
1242
1243     If both a relative and absolute control position are specified for a control point's axis, the relative
1244     position will be used.
1245
1246     Relative and absolute positions can be mixed, for example it is valid to set a relative control1 x
1247     and an absolute control1 y.
1248
1249     \sa control1X, control1Y, control2X, control2Y
1250 */
1251
1252 qreal QQuickPathCubic::relativeControl1X() const
1253 {
1254     return _relativeControl1X;
1255 }
1256
1257 void QQuickPathCubic::setRelativeControl1X(qreal x)
1258 {
1259     if (_relativeControl1X.isNull || _relativeControl1X != x) {
1260         _relativeControl1X = x;
1261         emit relativeControl1XChanged();
1262         emit changed();
1263     }
1264 }
1265
1266 bool QQuickPathCubic::hasRelativeControl1X()
1267 {
1268     return _relativeControl1X.isValid();
1269 }
1270
1271 qreal QQuickPathCubic::relativeControl1Y() const
1272 {
1273     return _relativeControl1Y;
1274 }
1275
1276 void QQuickPathCubic::setRelativeControl1Y(qreal y)
1277 {
1278     if (_relativeControl1Y.isNull || _relativeControl1Y != y) {
1279         _relativeControl1Y = y;
1280         emit relativeControl1YChanged();
1281         emit changed();
1282     }
1283 }
1284
1285 bool QQuickPathCubic::hasRelativeControl1Y()
1286 {
1287     return _relativeControl1Y.isValid();
1288 }
1289
1290 qreal QQuickPathCubic::relativeControl2X() const
1291 {
1292     return _relativeControl2X;
1293 }
1294
1295 void QQuickPathCubic::setRelativeControl2X(qreal x)
1296 {
1297     if (_relativeControl2X.isNull || _relativeControl2X != x) {
1298         _relativeControl2X = x;
1299         emit relativeControl2XChanged();
1300         emit changed();
1301     }
1302 }
1303
1304 bool QQuickPathCubic::hasRelativeControl2X()
1305 {
1306     return _relativeControl2X.isValid();
1307 }
1308
1309 qreal QQuickPathCubic::relativeControl2Y() const
1310 {
1311     return _relativeControl2Y;
1312 }
1313
1314 void QQuickPathCubic::setRelativeControl2Y(qreal y)
1315 {
1316     if (_relativeControl2Y.isNull || _relativeControl2Y != y) {
1317         _relativeControl2Y = y;
1318         emit relativeControl2YChanged();
1319         emit changed();
1320     }
1321 }
1322
1323 bool QQuickPathCubic::hasRelativeControl2Y()
1324 {
1325     return _relativeControl2Y.isValid();
1326 }
1327
1328 void QQuickPathCubic::addToPath(QPainterPath &path, const QQuickPathData &data)
1329 {
1330     const QPointF &prevPoint = path.currentPosition();
1331     QPointF controlPoint1(hasRelativeControl1X() ? prevPoint.x() + relativeControl1X() : control1X(),
1332                           hasRelativeControl1Y() ? prevPoint.y() + relativeControl1Y() : control1Y());
1333     QPointF controlPoint2(hasRelativeControl2X() ? prevPoint.x() + relativeControl2X() : control2X(),
1334                           hasRelativeControl2Y() ? prevPoint.y() + relativeControl2Y() : control2Y());
1335     path.cubicTo(controlPoint1, controlPoint2, positionForCurve(data, path.currentPosition()));
1336 }
1337
1338 /****************************************************************************/
1339
1340 /*!
1341     \qmlclass PathCurve QQuickPathCurve
1342     \inqmlmodule QtQuick 2
1343     \ingroup qml-view-elements
1344     \brief The PathCurve defines a point on a Catmull-Rom curve.
1345
1346     PathCurve provides an easy way to specify a curve passing directly through a set of points.
1347     Typically multiple PathCurves are used in a series, as the following example demonstrates:
1348
1349     \snippet doc/src/snippets/qml/path/basiccurve.qml 0
1350
1351     This example produces the following path (with the starting point and PathCurve points
1352     highlighted in red):
1353
1354     \image declarative-pathcurve.png
1355
1356     \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathSvg
1357 */
1358
1359 /*!
1360     \qmlproperty real QtQuick2::PathCurve::x
1361     \qmlproperty real QtQuick2::PathCurve::y
1362
1363     Defines the end point of the curve.
1364
1365     \sa relativeX, relativeY
1366 */
1367
1368 /*!
1369     \qmlproperty real QtQuick2::PathCurve::relativeX
1370     \qmlproperty real QtQuick2::PathCurve::relativeY
1371
1372     Defines the end point of the curve relative to its start.
1373
1374     If both a relative and absolute end position are specified for a single axis, the relative
1375     position will be used.
1376
1377     Relative and absolute positions can be mixed, for example it is valid to set a relative x
1378     and an absolute y.
1379
1380     \sa x, y
1381 */
1382
1383 inline QPointF previousPathPosition(const QPainterPath &path)
1384 {
1385     int count = path.elementCount();
1386     if (count < 1)
1387         return QPointF();
1388
1389     int index = path.elementAt(count-1).type == QPainterPath::CurveToDataElement ? count - 4 : count - 2;
1390     return index > -1 ? QPointF(path.elementAt(index)) : path.pointAtPercent(0);
1391 }
1392
1393 void QQuickPathCatmullRomCurve::addToPath(QPainterPath &path, const QQuickPathData &data)
1394 {
1395     //here we convert catmull-rom spline to bezier for use in QPainterPath.
1396     //basic conversion algorithm:
1397     //  catmull-rom points * inverse bezier matrix * catmull-rom matrix = bezier points
1398     //each point in the catmull-rom spline produces a bezier endpoint + 2 control points
1399     //calculations for each point use a moving window of 4 points
1400     //  (previous 2 points + current point + next point)
1401     QPointF prevFar, prev, point, next;
1402
1403     //get previous points
1404     int index = data.index - 1;
1405     QQuickCurve *curve = index == -1 ? 0 : data.curves.at(index);
1406     if (qobject_cast<QQuickPathCatmullRomCurve*>(curve)) {
1407         prev = path.currentPosition();
1408         prevFar = previousPathPosition(path);
1409     } else {
1410         prev = path.currentPosition();
1411         bool prevFarSet = false;
1412         if (index == -1 && data.curves.count() > 1) {
1413             if (qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(data.curves.count()-1))) {
1414                 //TODO: profile and optimize
1415                 QPointF pos = prev;
1416                 QQuickPathData loopData;
1417                 loopData.endPoint = data.endPoint;
1418                 loopData.curves = data.curves;
1419                 for (int i = data.index; i < data.curves.count(); ++i) {
1420                     loopData.index = i;
1421                     pos = positionForCurve(loopData, pos);
1422                     if (i == data.curves.count()-2)
1423                         prevFar = pos;
1424                 }
1425                 if (pos == QPointF(path.elementAt(0))) {
1426                     //this is a closed path starting and ending with catmull-rom segments.
1427                     //we try to smooth the join point
1428                     prevFarSet = true;
1429                 }
1430             }
1431         }
1432         if (!prevFarSet)
1433             prevFar = prev;
1434     }
1435
1436     //get current point
1437     point = positionForCurve(data, path.currentPosition());
1438
1439     //get next point
1440     index = data.index + 1;
1441     if (index < data.curves.count() && qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(index))) {
1442         QQuickPathData nextData;
1443         nextData.index = index;
1444         nextData.endPoint = data.endPoint;
1445         nextData.curves = data.curves;
1446         next = positionForCurve(nextData, point);
1447     } else {
1448         if (point == QPointF(path.elementAt(0)) && qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(0))) {
1449             //this is a closed path starting and ending with catmull-rom segments.
1450             //we try to smooth the join point
1451             next = QPointF(path.elementAt(3));  //the first catmull-rom point
1452         } else
1453             next = point;
1454     }
1455
1456     /*
1457         full conversion matrix (inverse bezier * catmull-rom):
1458         0.000,  1.000,  0.000,  0.000,
1459         -0.167,  1.000,  0.167,  0.000,
1460         0.000,  0.167,  1.000, -0.167,
1461         0.000,  0.000,  1.000,  0.000
1462
1463         conversion doesn't require full matrix multiplication,
1464         so below we simplify
1465     */
1466     QPointF control1(prevFar.x() * qreal(-0.167) +
1467                      prev.x() +
1468                      point.x() * qreal(0.167),
1469                      prevFar.y() * qreal(-0.167) +
1470                      prev.y() +
1471                      point.y() * qreal(0.167));
1472
1473     QPointF control2(prev.x() * qreal(0.167) +
1474                      point.x() +
1475                      next.x() * qreal(-0.167),
1476                      prev.y() * qreal(0.167) +
1477                      point.y() +
1478                      next.y() * qreal(-0.167));
1479
1480     path.cubicTo(control1, control2, point);
1481 }
1482
1483 /****************************************************************************/
1484
1485 /*!
1486     \qmlclass PathArc QQuickPathArc
1487     \inqmlmodule QtQuick 2
1488     \ingroup qml-view-elements
1489     \brief The PathArc defines an arc with the given radius.
1490
1491     PathArc provides a simple way of specifying an arc that ends at a given position
1492     and uses the specified radius. It is modeled after the SVG elliptical arc command.
1493
1494     The following QML produces the path shown below:
1495     \table
1496     \row
1497     \li \image declarative-patharc.png
1498     \li \snippet doc/src/snippets/qml/path/basicarc.qml 0
1499     \endtable
1500
1501     Note that a single PathArc cannot be used to specify a circle. Instead, you can
1502     use two PathArc elements, each specifying half of the circle.
1503
1504     \sa Path, PathLine, PathQuad, PathCubic, PathCurve, PathSvg
1505 */
1506
1507 /*!
1508     \qmlproperty real QtQuick2::PathArc::x
1509     \qmlproperty real QtQuick2::PathArc::y
1510
1511     Defines the end point of the arc.
1512
1513     \sa relativeX, relativeY
1514 */
1515
1516 /*!
1517     \qmlproperty real QtQuick2::PathArc::relativeX
1518     \qmlproperty real QtQuick2::PathArc::relativeY
1519
1520     Defines the end point of the arc relative to its start.
1521
1522     If both a relative and absolute end position are specified for a single axis, the relative
1523     position will be used.
1524
1525     Relative and absolute positions can be mixed, for example it is valid to set a relative x
1526     and an absolute y.
1527
1528     \sa x, y
1529 */
1530
1531 /*!
1532     \qmlproperty real QtQuick2::PathArc::radiusX
1533     \qmlproperty real QtQuick2::PathArc::radiusY
1534
1535     Defines the radius of the arc.
1536
1537     The following QML demonstrates how different radius values can be used to change
1538     the shape of the arc:
1539     \table
1540     \row
1541     \li \image declarative-arcradius.png
1542     \li \snippet doc/src/snippets/qml/path/arcradius.qml 0
1543     \endtable
1544 */
1545
1546 qreal QQuickPathArc::radiusX() const
1547 {
1548     return _radiusX;
1549 }
1550
1551 void QQuickPathArc::setRadiusX(qreal radius)
1552 {
1553     if (_radiusX == radius)
1554         return;
1555
1556     _radiusX = radius;
1557     emit radiusXChanged();
1558 }
1559
1560 qreal QQuickPathArc::radiusY() const
1561 {
1562     return _radiusY;
1563 }
1564
1565 void QQuickPathArc::setRadiusY(qreal radius)
1566 {
1567     if (_radiusY == radius)
1568         return;
1569
1570     _radiusY = radius;
1571     emit radiusYChanged();
1572 }
1573
1574 /*!
1575     \qmlproperty bool QtQuick2::PathArc::useLargeArc
1576     Whether to use a large arc as defined by the arc points.
1577
1578     Given fixed start and end positions, radius, and direction,
1579     there are two possible arcs that can fit the data. useLargeArc
1580     is used to distinguish between these. For example, the following
1581     QML can produce either of the two illustrated arcs below by
1582     changing the value of useLargeArc.
1583
1584     \table
1585     \row
1586     \li \image declarative-largearc.png
1587     \li \snippet doc/src/snippets/qml/path/largearc.qml 0
1588     \endtable
1589
1590     The default value is false.
1591 */
1592
1593 bool QQuickPathArc::useLargeArc() const
1594 {
1595     return _useLargeArc;
1596 }
1597
1598 void QQuickPathArc::setUseLargeArc(bool largeArc)
1599 {
1600     if (_useLargeArc == largeArc)
1601         return;
1602
1603     _useLargeArc = largeArc;
1604     emit useLargeArcChanged();
1605 }
1606
1607 /*!
1608     \qmlproperty enum QtQuick2::PathArc::direction
1609
1610     Defines the direction of the arc. Possible values are
1611     PathArc.Clockwise (default) and PathArc.Counterclockwise.
1612
1613     The following QML can produce either of the two illustrated arcs below
1614     by changing the value of direction.
1615     \table
1616     \row
1617     \li \image declarative-arcdirection.png
1618     \li \snippet doc/src/snippets/qml/path/arcdirection.qml 0
1619     \endtable
1620
1621     \sa useLargeArc
1622 */
1623
1624 QQuickPathArc::ArcDirection QQuickPathArc::direction() const
1625 {
1626     return _direction;
1627 }
1628
1629 void QQuickPathArc::setDirection(ArcDirection direction)
1630 {
1631     if (_direction == direction)
1632         return;
1633
1634     _direction = direction;
1635     emit directionChanged();
1636 }
1637
1638 void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data)
1639 {
1640     const QPointF &startPoint = path.currentPosition();
1641     const QPointF &endPoint = positionForCurve(data, startPoint);
1642     QQuickSvgParser::pathArc(path,
1643             _radiusX,
1644             _radiusY,
1645             0,  //xAxisRotation
1646             _useLargeArc,
1647             _direction == Clockwise ? 1 : 0,
1648             endPoint.x(),
1649             endPoint.y(),
1650             startPoint.x(), startPoint.y());
1651 }
1652
1653 /****************************************************************************/
1654
1655 /*!
1656     \qmlclass PathSvg QQuickPathSvg
1657     \inqmlmodule QtQuick 2
1658     \ingroup qml-view-elements
1659     \brief The PathSvg defines a path using an SVG path data string.
1660
1661     The following QML produces the path shown below:
1662     \table
1663     \row
1664     \li \image declarative-pathsvg.png
1665     \li
1666     \qml
1667     Path {
1668         startX: 50; startY: 50
1669         PathSvg { path: "L 150 50 L 100 150 z" }
1670     }
1671     \endqml
1672     \endtable
1673
1674     \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathCurve
1675 */
1676
1677 /*!
1678     \qmlproperty string QtQuick2::PathSvg::path
1679
1680     The SVG path data string specifying the path.
1681
1682     See \l {http://www.w3.org/TR/SVG/paths.html#PathData}{W3C SVG Path Data}
1683     for more details on this format.
1684 */
1685
1686 QString QQuickPathSvg::path() const
1687 {
1688     return _path;
1689 }
1690
1691 void QQuickPathSvg::setPath(const QString &path)
1692 {
1693     if (_path == path)
1694         return;
1695
1696     _path = path;
1697     emit pathChanged();
1698 }
1699
1700 void QQuickPathSvg::addToPath(QPainterPath &path, const QQuickPathData &)
1701 {
1702     QQuickSvgParser::parsePathDataFast(_path, path);
1703 }
1704
1705 /****************************************************************************/
1706
1707 /*!
1708     \qmlclass PathPercent QQuickPathPercent
1709     \inqmlmodule QtQuick 2
1710     \ingroup qml-view-elements
1711     \brief The PathPercent manipulates the way a path is interpreted.
1712
1713     PathPercent allows you to manipulate the spacing between items on a
1714     PathView's path. You can use it to bunch together items on part of
1715     the path, and spread them out on other parts of the path.
1716
1717     The examples below show the normal distribution of items along a path
1718     compared to a distribution which places 50% of the items along the
1719     PathLine section of the path.
1720     \table
1721     \row
1722     \li \image declarative-nopercent.png
1723     \li
1724     \qml
1725     PathView {
1726         // ...
1727         Path {
1728             startX: 20; startY: 0
1729             PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
1730             PathLine { x: 150; y: 80 }
1731             PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
1732         }
1733     }
1734     \endqml
1735     \row
1736     \li \image declarative-percent.png
1737     \li
1738     \qml
1739     PathView {
1740         // ...
1741         Path {
1742             startX: 20; startY: 0
1743             PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
1744             PathPercent { value: 0.25 }
1745             PathLine { x: 150; y: 80 }
1746             PathPercent { value: 0.75 }
1747             PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
1748             PathPercent { value: 1 }
1749         }
1750     }
1751     \endqml
1752     \endtable
1753
1754     \sa Path
1755 */
1756
1757 /*!
1758     \qmlproperty real QtQuick2::PathPercent::value
1759     The proportion of items that should be laid out up to this point.
1760
1761     This value should always be higher than the last value specified
1762     by a PathPercent at a previous position in the Path.
1763
1764     In the following example we have a Path made up of three PathLines.
1765     Normally, the items of the PathView would be laid out equally along
1766     this path, with an equal number of items per line segment. PathPercent
1767     allows us to specify that the first and third lines should each hold
1768     10% of the laid out items, while the second line should hold the remaining
1769     80%.
1770
1771     \qml
1772     PathView {
1773         // ...
1774         Path {
1775             startX: 0; startY: 0
1776             PathLine { x:100; y: 0; }
1777             PathPercent { value: 0.1 }
1778             PathLine { x: 100; y: 100 }
1779             PathPercent { value: 0.9 }
1780             PathLine { x: 100; y: 0 }
1781             PathPercent { value: 1 }
1782         }
1783     }
1784     \endqml
1785 */
1786
1787 qreal QQuickPathPercent::value() const
1788 {
1789     return _value;
1790 }
1791
1792 void QQuickPathPercent::setValue(qreal value)
1793 {
1794     if (_value != value) {
1795         _value = value;
1796         emit valueChanged();
1797         emit changed();
1798     }
1799 }
1800 QT_END_NAMESPACE