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