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