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