1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qquickpath_p.h"
43 #include "qquickpath_p_p.h"
44 #include "qquicksvgparser_p.h"
49 #include <private/qbezier_p.h>
50 #include <QtCore/qmath.h>
51 #include <QtCore/qnumeric.h>
56 \qmlclass PathElement QQuickPathElement
57 \inqmlmodule QtQuick 2
58 \ingroup qml-view-elements
59 \brief PathElement is the base path type.
61 This type is the base for all path types. It cannot
64 \sa Path, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
68 \qmlclass Path QQuickPath
69 \inqmlmodule QtQuick 2
70 \ingroup qml-view-elements
71 \brief A Path object defines a path for use by \l PathView.
73 A Path is composed of one or more path segments - PathLine, PathQuad,
74 PathCubic, PathArc, PathCurve, PathSvg.
76 The spacing of the items along the Path can be adjusted via a
79 PathAttribute allows named attributes with values to be defined
82 \sa PathView, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
84 QQuickPath::QQuickPath(QObject *parent)
85 : QObject(*(new QQuickPathPrivate), parent)
89 QQuickPath::~QQuickPath()
94 \qmlproperty real QtQuick2::Path::startX
95 \qmlproperty real QtQuick2::Path::startY
96 These properties hold the starting position of the path.
98 qreal QQuickPath::startX() const
100 Q_D(const QQuickPath);
101 return d->startX.isNull ? 0 : d->startX.value;
104 void QQuickPath::setStartX(qreal x)
107 if (d->startX.isValid() && qFuzzyCompare(x, d->startX))
110 emit startXChanged();
114 bool QQuickPath::hasStartX() const
116 Q_D(const QQuickPath);
117 return d->startX.isValid();
120 qreal QQuickPath::startY() const
122 Q_D(const QQuickPath);
123 return d->startY.isNull ? 0 : d->startY.value;
126 void QQuickPath::setStartY(qreal y)
129 if (d->startY.isValid() && qFuzzyCompare(y, d->startY))
132 emit startYChanged();
136 bool QQuickPath::hasStartY() const
138 Q_D(const QQuickPath);
139 return d->startY.isValid();
143 \qmlproperty bool QtQuick2::Path::closed
144 This property holds whether the start and end of the path are identical.
146 bool QQuickPath::isClosed() const
148 Q_D(const QQuickPath);
152 bool QQuickPath::hasEnd() const
154 Q_D(const QQuickPath);
155 for (int i = d->_pathElements.count() - 1; i > -1; --i) {
156 if (QQuickCurve *curve = qobject_cast<QQuickCurve *>(d->_pathElements.at(i))) {
157 if ((!curve->hasX() && !curve->hasRelativeX()) || (!curve->hasY() && !curve->hasRelativeY()))
163 return hasStartX() && hasStartY();
167 \qmlproperty list<PathElement> QtQuick2::Path::pathElements
168 This property holds the objects composing the path.
172 A path can contain the following path objects:
174 \li \l PathLine - a straight line to a given position.
175 \li \l PathQuad - a quadratic Bezier curve to a given position with a control point.
176 \li \l PathCubic - a cubic Bezier curve to a given position with two control points.
177 \li \l PathArc - an arc to a given position with a radius.
178 \li \l PathSvg - a path specified as an SVG path data string.
179 \li \l PathCurve - a point on a Catmull-Rom curve.
180 \li \l PathAttribute - an attribute at a given position in the path.
181 \li \l PathPercent - a way to spread out items along various segments of the path.
184 \snippet doc/src/snippets/qml/pathview/pathattributes.qml 2
187 QQmlListProperty<QQuickPathElement> QQuickPath::pathElements()
190 return QQmlListProperty<QQuickPathElement>(this, d->_pathElements);
193 void QQuickPath::interpolate(int idx, const QString &name, qreal value)
196 interpolate(d->_attributePoints, idx, name, value);
199 void QQuickPath::interpolate(QList<AttributePoint> &attributePoints, int idx, const QString &name, qreal value)
205 qreal lastPercent = 0;
206 int search = idx - 1;
208 const AttributePoint &point = attributePoints.at(search);
209 if (point.values.contains(name)) {
210 lastValue = point.values.value(name);
211 lastPercent = point.origpercent;
219 const AttributePoint &curPoint = attributePoints.at(idx);
221 for (int ii = search; ii < idx; ++ii) {
222 AttributePoint &point = attributePoints[ii];
224 qreal val = lastValue + (value - lastValue) * (point.origpercent - lastPercent) / (curPoint.origpercent - lastPercent);
225 point.values.insert(name, val);
229 void QQuickPath::endpoint(const QString &name)
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);
246 void QQuickPath::endpoint(QList<AttributePoint> &attributePoints, const QString &name)
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);
262 static QString percentString(QLatin1String("_qfx_percent"));
264 void QQuickPath::processPath()
268 if (!d->componentComplete)
271 d->_pointCache.clear();
272 d->prevBez.isValid = false;
274 d->_path = createPath(QPointF(), QPointF(), d->_attributes, d->pathLength, d->_attributePoints, &d->closed);
279 QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &endPoint, const QStringList &attributes, qreal &pathLength, QList<AttributePoint> &attributePoints, bool *closed)
284 attributePoints.clear();
286 if (!d->componentComplete)
287 return QPainterPath();
291 AttributePoint first;
292 for (int ii = 0; ii < attributes.count(); ++ii)
293 first.values[attributes.at(ii)] = 0;
294 attributePoints << first;
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);
300 bool usesPercent = false;
302 foreach (QQuickPathElement *pathElement, d->_pathElements) {
303 if (QQuickCurve *curve = qobject_cast<QQuickCurve *>(pathElement)) {
306 data.endPoint = endPoint;
307 data.curves = d->_pathCurves;
308 curve->addToPath(path, data);
310 p.origpercent = path.length();
311 attributePoints << p;
313 } else if (QQuickPathAttribute *attribute = qobject_cast<QQuickPathAttribute *>(pathElement)) {
314 AttributePoint &point = attributePoints.last();
315 point.values[attribute->name()] = attribute->value();
316 interpolate(attributePoints, attributePoints.count() - 1, attribute->name(), attribute->value());
317 } else if (QQuickPathPercent *percent = qobject_cast<QQuickPathPercent *>(pathElement)) {
318 AttributePoint &point = attributePoints.last();
319 point.values[percentString] = percent->value();
320 interpolate(attributePoints, attributePoints.count() - 1, percentString, percent->value());
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));
331 if (usesPercent && !last.values.contains(percentString)) {
332 d->_attributePoints.last().values[percentString] = 1;
333 interpolate(d->_attributePoints.count() - 1, percentString, 1);
338 qreal length = path.length();
339 qreal prevpercent = 0;
340 qreal prevorigpercent = 0;
341 for (int ii = 0; ii < attributePoints.count(); ++ii) {
342 const AttributePoint &point = attributePoints.at(ii);
343 if (point.values.contains(percentString)) { //special string for QQuickPathPercent
345 qreal scale = (attributePoints[ii].origpercent/length - prevorigpercent) /
346 (point.values.value(percentString)-prevpercent);
347 attributePoints[ii].scale = scale;
349 attributePoints[ii].origpercent /= length;
350 attributePoints[ii].percent = point.values.value(percentString);
351 prevorigpercent = attributePoints[ii].origpercent;
352 prevpercent = attributePoints[ii].percent;
354 attributePoints[ii].origpercent /= length;
355 attributePoints[ii].percent = attributePoints[ii].origpercent;
360 QPointF end = path.currentPosition();
361 *closed = length > 0 && startX == end.x() && startY == end.y();
368 void QQuickPath::classBegin()
371 d->componentComplete = false;
374 void QQuickPath::componentComplete()
378 d->componentComplete = true;
380 // First gather up all the attributes
381 foreach (QQuickPathElement *pathElement, d->_pathElements) {
382 if (QQuickCurve *curve =
383 qobject_cast<QQuickCurve *>(pathElement))
384 d->_pathCurves.append(curve);
385 else if (QQuickPathAttribute *attribute =
386 qobject_cast<QQuickPathAttribute *>(pathElement))
387 attrs.insert(attribute->name());
389 d->_attributes = attrs.toList();
393 foreach (QQuickPathElement *pathElement, d->_pathElements)
394 connect(pathElement, SIGNAL(changed()), this, SLOT(processPath()));
397 QPainterPath QQuickPath::path() const
399 Q_D(const QQuickPath);
403 QStringList QQuickPath::attributes() const
405 Q_D(const QQuickPath);
406 if (!d->componentComplete) {
409 // First gather up all the attributes
410 foreach (QQuickPathElement *pathElement, d->_pathElements) {
411 if (QQuickPathAttribute *attribute =
412 qobject_cast<QQuickPathAttribute *>(pathElement))
413 attrs.insert(attribute->name());
415 return attrs.toList();
417 return d->_attributes;
420 static inline QBezier nextBezier(const QPainterPath &path, int *current, qreal *bezLength, bool reverse = false)
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);
428 case QPainterPath::MoveToElement:
430 case QPainterPath::LineToElement:
432 QLineF line(path.elementAt(i-1), e);
433 *bezLength = line.length();
434 QPointF a = path.elementAt(i-1);
435 QPointF delta = e - a;
437 return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
439 case QPainterPath::CurveToElement:
441 QBezier b = QBezier::fromPoints(path.elementAt(i-1),
444 path.elementAt(i+2));
445 *bezLength = b.length();
453 *current = lastElement;
458 //derivative of the equation
459 static inline qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d)
461 return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
464 void QQuickPath::createPointCache() const
466 Q_D(const QQuickPath);
467 qreal pathLength = d->pathLength;
468 if (pathLength <= 0 || qIsNaN(pathLength))
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);
476 int currElement = -1;
478 QBezier currBez = nextBezier(d->_path, &currElement, &bezLength);
479 qreal currLength = bezLength;
480 qreal epc = currLength / pathLength;
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);
492 qreal spc = prevOrigPercent + elementPercent * point.scale;
495 if (currElement > lastElement)
497 currBez = nextBezier(d->_path, &currElement, &bezLength);
498 if (bezLength == 0.0) {
499 currLength = pathLength;
503 currLength += bezLength;
504 epc = currLength / pathLength;
506 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
507 d->_pointCache[i] = currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
510 prevOrigPercent = point.origpercent;
511 prevPercent = point.percent;
516 void QQuickPath::invalidateSequentialHistory() const
518 Q_D(const QQuickPath);
519 d->prevBez.isValid = false;
522 QPointF QQuickPath::sequentialPointAt(qreal p, qreal *angle) const
524 Q_D(const QQuickPath);
525 return sequentialPointAt(d->_path, d->pathLength, d->_attributePoints, d->prevBez, p, angle);
528 QPointF QQuickPath::sequentialPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
530 Q_ASSERT(p >= 0.0 && p <= 1.0);
532 if (!prevBez.isValid)
533 return p > .5 ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
534 forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
536 return p < prevBez.p ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
537 forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
540 QPointF QQuickPath::forwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
542 if (pathLength <= 0 || qIsNaN(pathLength))
543 return path.pointAtPercent(0); //expensive?
545 const int lastElement = path.elementCount() - 1;
546 bool haveCachedBez = prevBez.isValid;
547 int currElement = haveCachedBez ? prevBez.element : -1;
548 qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
549 QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength);
550 qreal currLength = haveCachedBez ? prevBez.currLength : bezLength;
551 qreal epc = currLength / pathLength;
553 //find which set we are in
554 qreal prevPercent = 0;
555 qreal prevOrigPercent = 0;
556 for (int ii = 0; ii < attributePoints.count(); ++ii) {
558 const AttributePoint &point = attributePoints.at(ii);
559 if (percent < point.percent || ii == attributePoints.count() - 1) {
560 qreal elementPercent = (percent - prevPercent);
562 qreal spc = prevOrigPercent + elementPercent * point.scale;
565 Q_ASSERT(!(currElement > lastElement));
566 Q_UNUSED(lastElement);
567 currBez = nextBezier(path, &currElement, &bezLength);
568 currLength += bezLength;
569 epc = currLength / pathLength;
571 prevBez.element = currElement;
572 prevBez.bezLength = bezLength;
573 prevBez.currLength = currLength;
574 prevBez.bezier = currBez;
576 prevBez.isValid = true;
578 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
581 qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
582 qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
583 *angle = QLineF(0, 0, m1, m2).angle();
586 return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
588 prevOrigPercent = point.origpercent;
589 prevPercent = point.percent;
595 //ideally this should be merged with forwardsPointAt
596 QPointF QQuickPath::backwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
598 if (pathLength <= 0 || qIsNaN(pathLength))
599 return path.pointAtPercent(0);
601 const int firstElement = 1; //element 0 is always a MoveTo, which we ignore
602 bool haveCachedBez = prevBez.isValid;
603 int currElement = haveCachedBez ? prevBez.element : path.elementCount();
604 qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
605 QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength, true /*reverse*/);
606 qreal currLength = haveCachedBez ? prevBez.currLength : pathLength;
607 qreal prevLength = currLength - bezLength;
608 qreal epc = prevLength / pathLength;
610 for (int ii = attributePoints.count() - 1; ii > 0; --ii) {
612 const AttributePoint &point = attributePoints.at(ii);
613 const AttributePoint &prevPoint = attributePoints.at(ii-1);
614 if (percent > prevPoint.percent || ii == 1) {
615 qreal elementPercent = (percent - prevPoint.percent);
617 qreal spc = prevPoint.origpercent + elementPercent * point.scale;
620 Q_ASSERT(!(currElement < firstElement));
621 Q_UNUSED(firstElement);
622 currBez = nextBezier(path, &currElement, &bezLength, true /*reverse*/);
623 //special case for first element is to avoid floating point math
624 //causing an epc that never hits 0.
625 currLength = (currElement == firstElement) ? bezLength : prevLength;
626 prevLength = currLength - bezLength;
627 epc = prevLength / pathLength;
629 prevBez.element = currElement;
630 prevBez.bezLength = bezLength;
631 prevBez.currLength = currLength;
632 prevBez.bezier = currBez;
634 prevBez.isValid = true;
636 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
639 qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
640 qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
641 *angle = QLineF(0, 0, m1, m2).angle();
644 return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
651 QPointF QQuickPath::pointAt(qreal p) const
653 Q_D(const QQuickPath);
654 if (d->_pointCache.isEmpty()) {
656 if (d->_pointCache.isEmpty())
660 const int pointCacheSize = d->_pointCache.size();
661 qreal idxf = p*pointCacheSize;
662 int idx1 = qFloor(idxf);
663 qreal delta = idxf - idx1;
664 if (idx1 >= pointCacheSize)
665 idx1 = pointCacheSize - 1;
670 return d->_pointCache.at(idx1);
672 // interpolate between the two points.
673 int idx2 = qCeil(idxf);
674 if (idx2 >= pointCacheSize)
675 idx2 = pointCacheSize - 1;
679 QPointF p1 = d->_pointCache.at(idx1);
680 QPointF p2 = d->_pointCache.at(idx2);
681 QPointF pos = p1 * (1.0-delta) + p2 * delta;
686 qreal QQuickPath::attributeAt(const QString &name, qreal percent) const
688 Q_D(const QQuickPath);
689 if (percent < 0 || percent > 1)
692 for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
693 const AttributePoint &point = d->_attributePoints.at(ii);
695 if (point.percent == percent) {
696 return point.values.value(name);
697 } else if (point.percent > percent) {
699 ii?(d->_attributePoints.at(ii - 1).values.value(name)):0;
701 ii?(d->_attributePoints.at(ii - 1).percent):0;
702 qreal curValue = point.values.value(name);
703 qreal curPercent = point.percent;
705 return lastValue + (curValue - lastValue) * (percent - lastPercent) / (curPercent - lastPercent);
712 /****************************************************************************/
714 qreal QQuickCurve::x() const
716 return _x.isNull ? 0 : _x.value;
719 void QQuickCurve::setX(qreal x)
721 if (_x.isNull || _x != x) {
728 bool QQuickCurve::hasX()
733 qreal QQuickCurve::y() const
735 return _y.isNull ? 0 : _y.value;
738 void QQuickCurve::setY(qreal y)
740 if (_y.isNull || _y != y) {
747 bool QQuickCurve::hasY()
752 qreal QQuickCurve::relativeX() const
757 void QQuickCurve::setRelativeX(qreal x)
759 if (_relativeX.isNull || _relativeX != x) {
761 emit relativeXChanged();
766 bool QQuickCurve::hasRelativeX()
768 return _relativeX.isValid();
771 qreal QQuickCurve::relativeY() const
776 void QQuickCurve::setRelativeY(qreal y)
778 if (_relativeY.isNull || _relativeY != y) {
780 emit relativeYChanged();
785 bool QQuickCurve::hasRelativeY()
787 return _relativeY.isValid();
790 /****************************************************************************/
793 \qmlclass PathAttribute QQuickPathAttribute
794 \inqmlmodule QtQuick 2
795 \ingroup qml-view-elements
796 \brief The PathAttribute allows setting an attribute at a given position in a Path.
798 The PathAttribute object allows attributes consisting of a name and
799 a value to be specified for various points along a path. The
800 attributes are exposed to the delegate as
801 \l{qdeclarativeintroduction.html#attached-properties} {Attached Properties}.
802 The value of an attribute at any particular point along the path is interpolated
803 from the PathAttributes bounding that point.
805 The example below shows a path with the items scaled to 30% with
806 opacity 50% at the top of the path and scaled 100% with opacity
807 100% at the bottom. Note the use of the PathView.iconScale and
808 PathView.iconOpacity attached properties to set the scale and opacity
813 \li \image declarative-pathattribute.png
815 \snippet doc/src/snippets/qml/pathview/pathattributes.qml 0
816 (see the PathView documentation for the specification of ContactModel.qml
817 used for ContactModel above.)
825 \qmlproperty string QtQuick2::PathAttribute::name
826 This property holds the name of the attribute to change.
828 This attribute will be available to the delegate as PathView.<name>
830 Note that using an existing Item property name such as "opacity" as an
831 attribute is allowed. This is because path attributes add a new
832 \l{qdeclarativeintroduction.html#attached-properties} {Attached Property}
833 which in no way clashes with existing properties.
837 the name of the attribute to change.
840 QString QQuickPathAttribute::name() const
845 void QQuickPathAttribute::setName(const QString &name)
854 \qmlproperty real QtQuick2::PathAttribute::value
855 This property holds the value for the attribute.
857 The value specified can be used to influence the visual appearance
858 of an item along the path. For example, the following Path specifies
859 an attribute named \e itemRotation, which has the value \e 0 at the
860 beginning of the path, and the value 90 at the end of the path.
866 PathAttribute { name: "itemRotation"; value: 0 }
867 PathLine { x: 100; y: 100 }
868 PathAttribute { name: "itemRotation"; value: 90 }
872 In our delegate, we can then bind the \e rotation property to the
873 \l{qdeclarativeintroduction.html#attached-properties} {Attached Property}
874 \e PathView.itemRotation created for this attribute.
878 width: 10; height: 10
879 rotation: PathView.itemRotation
883 As each item is positioned along the path, it will be rotated accordingly:
884 an item at the beginning of the path with be not be rotated, an item at
885 the end of the path will be rotated 90 degrees, and an item mid-way along
886 the path will be rotated 45 degrees.
890 the new value of the attribute.
892 qreal QQuickPathAttribute::value() const
897 void QQuickPathAttribute::setValue(qreal value)
899 if (_value != value) {
906 /****************************************************************************/
909 \qmlclass PathLine QQuickPathLine
910 \inqmlmodule QtQuick 2
911 \ingroup qml-view-elements
912 \brief The PathLine defines a straight line.
914 The example below creates a path consisting of a straight line from
919 startX: 0; startY: 100
920 PathLine { x: 200; y: 100 }
924 \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
928 \qmlproperty real QtQuick2::PathLine::x
929 \qmlproperty real QtQuick2::PathLine::y
931 Defines the end point of the line.
933 \sa relativeX, relativeY
937 \qmlproperty real QtQuick2::PathLine::relativeX
938 \qmlproperty real QtQuick2::PathLine::relativeY
940 Defines the end point of the line relative to its start.
942 If both a relative and absolute end position are specified for a single axis, the relative
943 position will be used.
945 Relative and absolute positions can be mixed, for example it is valid to set a relative x
951 inline QPointF positionForCurve(const QQuickPathData &data, const QPointF &prevPoint)
953 QQuickCurve *curve = data.curves.at(data.index);
954 bool isEnd = data.index == data.curves.size() - 1;
955 return QPointF(curve->hasRelativeX() ? prevPoint.x() + curve->relativeX() : !isEnd || curve->hasX() ? curve->x() : data.endPoint.x(),
956 curve->hasRelativeY() ? prevPoint.y() + curve->relativeY() : !isEnd || curve->hasY() ? curve->y() : data.endPoint.y());
959 void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data)
961 path.lineTo(positionForCurve(data, path.currentPosition()));
964 /****************************************************************************/
967 \qmlclass PathQuad QQuickPathQuad
968 \inqmlmodule QtQuick 2
969 \ingroup qml-view-elements
970 \brief The PathQuad defines a quadratic Bezier curve with a control point.
972 The following QML produces the path shown below:
975 \li \image declarative-pathquad.png
980 PathQuad { x: 200; y: 0; controlX: 100; controlY: 150 }
985 \sa Path, PathCubic, PathLine, PathArc, PathCurve, PathSvg
989 \qmlproperty real QtQuick2::PathQuad::x
990 \qmlproperty real QtQuick2::PathQuad::y
992 Defines the end point of the curve.
994 \sa relativeX, relativeY
998 \qmlproperty real QtQuick2::PathQuad::relativeX
999 \qmlproperty real QtQuick2::PathQuad::relativeY
1001 Defines the end point of the curve relative to its start.
1003 If both a relative and absolute end position are specified for a single axis, the relative
1004 position will be used.
1006 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1013 \qmlproperty real QtQuick2::PathQuad::controlX
1014 \qmlproperty real QtQuick2::PathQuad::controlY
1016 Defines the position of the control point.
1020 the x position of the control point.
1022 qreal QQuickPathQuad::controlX() const
1027 void QQuickPathQuad::setControlX(qreal x)
1029 if (_controlX != x) {
1031 emit controlXChanged();
1038 the y position of the control point.
1040 qreal QQuickPathQuad::controlY() const
1045 void QQuickPathQuad::setControlY(qreal y)
1047 if (_controlY != y) {
1049 emit controlYChanged();
1055 \qmlproperty real QtQuick2::PathCubic::relativeControlX
1056 \qmlproperty real QtQuick2::PathCubic::relativeControlY
1058 Defines the position of the control point relative to the curve's start.
1060 If both a relative and absolute control position are specified for a single axis, the relative
1061 position will be used.
1063 Relative and absolute positions can be mixed, for example it is valid to set a relative control x
1064 and an absolute control y.
1066 \sa controlX, controlY
1069 qreal QQuickPathQuad::relativeControlX() const
1071 return _relativeControlX;
1074 void QQuickPathQuad::setRelativeControlX(qreal x)
1076 if (_relativeControlX.isNull || _relativeControlX != x) {
1077 _relativeControlX = x;
1078 emit relativeControlXChanged();
1083 bool QQuickPathQuad::hasRelativeControlX()
1085 return _relativeControlX.isValid();
1088 qreal QQuickPathQuad::relativeControlY() const
1090 return _relativeControlY;
1093 void QQuickPathQuad::setRelativeControlY(qreal y)
1095 if (_relativeControlY.isNull || _relativeControlY != y) {
1096 _relativeControlY = y;
1097 emit relativeControlYChanged();
1102 bool QQuickPathQuad::hasRelativeControlY()
1104 return _relativeControlY.isValid();
1107 void QQuickPathQuad::addToPath(QPainterPath &path, const QQuickPathData &data)
1109 const QPointF &prevPoint = path.currentPosition();
1110 QPointF controlPoint(hasRelativeControlX() ? prevPoint.x() + relativeControlX() : controlX(),
1111 hasRelativeControlY() ? prevPoint.y() + relativeControlY() : controlY());
1112 path.quadTo(controlPoint, positionForCurve(data, path.currentPosition()));
1115 /****************************************************************************/
1118 \qmlclass PathCubic QQuickPathCubic
1119 \inqmlmodule QtQuick 2
1120 \ingroup qml-view-elements
1121 \brief The PathCubic defines a cubic Bezier curve with two control points.
1123 The following QML produces the path shown below:
1126 \li \image declarative-pathcubic.png
1130 startX: 20; startY: 0
1133 control1X: -10; control1Y: 90
1134 control2X: 210; control2Y: 90
1140 \sa Path, PathQuad, PathLine, PathArc, PathCurve, PathSvg
1144 \qmlproperty real QtQuick2::PathCubic::x
1145 \qmlproperty real QtQuick2::PathCubic::y
1147 Defines the end point of the curve.
1149 \sa relativeX, relativeY
1153 \qmlproperty real QtQuick2::PathCubic::relativeX
1154 \qmlproperty real QtQuick2::PathCubic::relativeY
1156 Defines the end point of the curve relative to its start.
1158 If both a relative and absolute end position are specified for a single axis, the relative
1159 position will be used.
1161 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1168 \qmlproperty real QtQuick2::PathCubic::control1X
1169 \qmlproperty real QtQuick2::PathCubic::control1Y
1171 Defines the position of the first control point.
1173 qreal QQuickPathCubic::control1X() const
1178 void QQuickPathCubic::setControl1X(qreal x)
1180 if (_control1X != x) {
1182 emit control1XChanged();
1187 qreal QQuickPathCubic::control1Y() const
1192 void QQuickPathCubic::setControl1Y(qreal y)
1194 if (_control1Y != y) {
1196 emit control1YChanged();
1202 \qmlproperty real QtQuick2::PathCubic::control2X
1203 \qmlproperty real QtQuick2::PathCubic::control2Y
1205 Defines the position of the second control point.
1207 qreal QQuickPathCubic::control2X() const
1212 void QQuickPathCubic::setControl2X(qreal x)
1214 if (_control2X != x) {
1216 emit control2XChanged();
1221 qreal QQuickPathCubic::control2Y() const
1226 void QQuickPathCubic::setControl2Y(qreal y)
1228 if (_control2Y != y) {
1230 emit control2YChanged();
1236 \qmlproperty real QtQuick2::PathCubic::relativeControl1X
1237 \qmlproperty real QtQuick2::PathCubic::relativeControl1Y
1238 \qmlproperty real QtQuick2::PathCubic::relativeControl2X
1239 \qmlproperty real QtQuick2::PathCubic::relativeControl2Y
1241 Defines the positions of the control points relative to the curve's start.
1243 If both a relative and absolute control position are specified for a control point's axis, the relative
1244 position will be used.
1246 Relative and absolute positions can be mixed, for example it is valid to set a relative control1 x
1247 and an absolute control1 y.
1249 \sa control1X, control1Y, control2X, control2Y
1252 qreal QQuickPathCubic::relativeControl1X() const
1254 return _relativeControl1X;
1257 void QQuickPathCubic::setRelativeControl1X(qreal x)
1259 if (_relativeControl1X.isNull || _relativeControl1X != x) {
1260 _relativeControl1X = x;
1261 emit relativeControl1XChanged();
1266 bool QQuickPathCubic::hasRelativeControl1X()
1268 return _relativeControl1X.isValid();
1271 qreal QQuickPathCubic::relativeControl1Y() const
1273 return _relativeControl1Y;
1276 void QQuickPathCubic::setRelativeControl1Y(qreal y)
1278 if (_relativeControl1Y.isNull || _relativeControl1Y != y) {
1279 _relativeControl1Y = y;
1280 emit relativeControl1YChanged();
1285 bool QQuickPathCubic::hasRelativeControl1Y()
1287 return _relativeControl1Y.isValid();
1290 qreal QQuickPathCubic::relativeControl2X() const
1292 return _relativeControl2X;
1295 void QQuickPathCubic::setRelativeControl2X(qreal x)
1297 if (_relativeControl2X.isNull || _relativeControl2X != x) {
1298 _relativeControl2X = x;
1299 emit relativeControl2XChanged();
1304 bool QQuickPathCubic::hasRelativeControl2X()
1306 return _relativeControl2X.isValid();
1309 qreal QQuickPathCubic::relativeControl2Y() const
1311 return _relativeControl2Y;
1314 void QQuickPathCubic::setRelativeControl2Y(qreal y)
1316 if (_relativeControl2Y.isNull || _relativeControl2Y != y) {
1317 _relativeControl2Y = y;
1318 emit relativeControl2YChanged();
1323 bool QQuickPathCubic::hasRelativeControl2Y()
1325 return _relativeControl2Y.isValid();
1328 void QQuickPathCubic::addToPath(QPainterPath &path, const QQuickPathData &data)
1330 const QPointF &prevPoint = path.currentPosition();
1331 QPointF controlPoint1(hasRelativeControl1X() ? prevPoint.x() + relativeControl1X() : control1X(),
1332 hasRelativeControl1Y() ? prevPoint.y() + relativeControl1Y() : control1Y());
1333 QPointF controlPoint2(hasRelativeControl2X() ? prevPoint.x() + relativeControl2X() : control2X(),
1334 hasRelativeControl2Y() ? prevPoint.y() + relativeControl2Y() : control2Y());
1335 path.cubicTo(controlPoint1, controlPoint2, positionForCurve(data, path.currentPosition()));
1338 /****************************************************************************/
1341 \qmlclass PathCurve QQuickPathCurve
1342 \inqmlmodule QtQuick 2
1343 \ingroup qml-view-elements
1344 \brief The PathCurve defines a point on a Catmull-Rom curve.
1346 PathCurve provides an easy way to specify a curve passing directly through a set of points.
1347 Typically multiple PathCurves are used in a series, as the following example demonstrates:
1349 \snippet doc/src/snippets/qml/path/basiccurve.qml 0
1351 This example produces the following path (with the starting point and PathCurve points
1352 highlighted in red):
1354 \image declarative-pathcurve.png
1356 \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathSvg
1360 \qmlproperty real QtQuick2::PathCurve::x
1361 \qmlproperty real QtQuick2::PathCurve::y
1363 Defines the end point of the curve.
1365 \sa relativeX, relativeY
1369 \qmlproperty real QtQuick2::PathCurve::relativeX
1370 \qmlproperty real QtQuick2::PathCurve::relativeY
1372 Defines the end point of the curve relative to its start.
1374 If both a relative and absolute end position are specified for a single axis, the relative
1375 position will be used.
1377 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1383 inline QPointF previousPathPosition(const QPainterPath &path)
1385 int count = path.elementCount();
1389 int index = path.elementAt(count-1).type == QPainterPath::CurveToDataElement ? count - 4 : count - 2;
1390 return index > -1 ? QPointF(path.elementAt(index)) : path.pointAtPercent(0);
1393 void QQuickPathCatmullRomCurve::addToPath(QPainterPath &path, const QQuickPathData &data)
1395 //here we convert catmull-rom spline to bezier for use in QPainterPath.
1396 //basic conversion algorithm:
1397 // catmull-rom points * inverse bezier matrix * catmull-rom matrix = bezier points
1398 //each point in the catmull-rom spline produces a bezier endpoint + 2 control points
1399 //calculations for each point use a moving window of 4 points
1400 // (previous 2 points + current point + next point)
1401 QPointF prevFar, prev, point, next;
1403 //get previous points
1404 int index = data.index - 1;
1405 QQuickCurve *curve = index == -1 ? 0 : data.curves.at(index);
1406 if (qobject_cast<QQuickPathCatmullRomCurve*>(curve)) {
1407 prev = path.currentPosition();
1408 prevFar = previousPathPosition(path);
1410 prev = path.currentPosition();
1411 bool prevFarSet = false;
1412 if (index == -1 && data.curves.count() > 1) {
1413 if (qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(data.curves.count()-1))) {
1414 //TODO: profile and optimize
1416 QQuickPathData loopData;
1417 loopData.endPoint = data.endPoint;
1418 loopData.curves = data.curves;
1419 for (int i = data.index; i < data.curves.count(); ++i) {
1421 pos = positionForCurve(loopData, pos);
1422 if (i == data.curves.count()-2)
1425 if (pos == QPointF(path.elementAt(0))) {
1426 //this is a closed path starting and ending with catmull-rom segments.
1427 //we try to smooth the join point
1437 point = positionForCurve(data, path.currentPosition());
1440 index = data.index + 1;
1441 if (index < data.curves.count() && qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(index))) {
1442 QQuickPathData nextData;
1443 nextData.index = index;
1444 nextData.endPoint = data.endPoint;
1445 nextData.curves = data.curves;
1446 next = positionForCurve(nextData, point);
1448 if (point == QPointF(path.elementAt(0)) && qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(0))) {
1449 //this is a closed path starting and ending with catmull-rom segments.
1450 //we try to smooth the join point
1451 next = QPointF(path.elementAt(3)); //the first catmull-rom point
1457 full conversion matrix (inverse bezier * catmull-rom):
1458 0.000, 1.000, 0.000, 0.000,
1459 -0.167, 1.000, 0.167, 0.000,
1460 0.000, 0.167, 1.000, -0.167,
1461 0.000, 0.000, 1.000, 0.000
1463 conversion doesn't require full matrix multiplication,
1464 so below we simplify
1466 QPointF control1(prevFar.x() * qreal(-0.167) +
1468 point.x() * qreal(0.167),
1469 prevFar.y() * qreal(-0.167) +
1471 point.y() * qreal(0.167));
1473 QPointF control2(prev.x() * qreal(0.167) +
1475 next.x() * qreal(-0.167),
1476 prev.y() * qreal(0.167) +
1478 next.y() * qreal(-0.167));
1480 path.cubicTo(control1, control2, point);
1483 /****************************************************************************/
1486 \qmlclass PathArc QQuickPathArc
1487 \inqmlmodule QtQuick 2
1488 \ingroup qml-view-elements
1489 \brief The PathArc defines an arc with the given radius.
1491 PathArc provides a simple way of specifying an arc that ends at a given position
1492 and uses the specified radius. It is modeled after the SVG elliptical arc command.
1494 The following QML produces the path shown below:
1497 \li \image declarative-patharc.png
1498 \li \snippet doc/src/snippets/qml/path/basicarc.qml 0
1501 Note that a single PathArc cannot be used to specify a circle. Instead, you can
1502 use two PathArc elements, each specifying half of the circle.
1504 \sa Path, PathLine, PathQuad, PathCubic, PathCurve, PathSvg
1508 \qmlproperty real QtQuick2::PathArc::x
1509 \qmlproperty real QtQuick2::PathArc::y
1511 Defines the end point of the arc.
1513 \sa relativeX, relativeY
1517 \qmlproperty real QtQuick2::PathArc::relativeX
1518 \qmlproperty real QtQuick2::PathArc::relativeY
1520 Defines the end point of the arc relative to its start.
1522 If both a relative and absolute end position are specified for a single axis, the relative
1523 position will be used.
1525 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1532 \qmlproperty real QtQuick2::PathArc::radiusX
1533 \qmlproperty real QtQuick2::PathArc::radiusY
1535 Defines the radius of the arc.
1537 The following QML demonstrates how different radius values can be used to change
1538 the shape of the arc:
1541 \li \image declarative-arcradius.png
1542 \li \snippet doc/src/snippets/qml/path/arcradius.qml 0
1546 qreal QQuickPathArc::radiusX() const
1551 void QQuickPathArc::setRadiusX(qreal radius)
1553 if (_radiusX == radius)
1557 emit radiusXChanged();
1560 qreal QQuickPathArc::radiusY() const
1565 void QQuickPathArc::setRadiusY(qreal radius)
1567 if (_radiusY == radius)
1571 emit radiusYChanged();
1575 \qmlproperty bool QtQuick2::PathArc::useLargeArc
1576 Whether to use a large arc as defined by the arc points.
1578 Given fixed start and end positions, radius, and direction,
1579 there are two possible arcs that can fit the data. useLargeArc
1580 is used to distinguish between these. For example, the following
1581 QML can produce either of the two illustrated arcs below by
1582 changing the value of useLargeArc.
1586 \li \image declarative-largearc.png
1587 \li \snippet doc/src/snippets/qml/path/largearc.qml 0
1590 The default value is false.
1593 bool QQuickPathArc::useLargeArc() const
1595 return _useLargeArc;
1598 void QQuickPathArc::setUseLargeArc(bool largeArc)
1600 if (_useLargeArc == largeArc)
1603 _useLargeArc = largeArc;
1604 emit useLargeArcChanged();
1608 \qmlproperty enum QtQuick2::PathArc::direction
1610 Defines the direction of the arc. Possible values are
1611 PathArc.Clockwise (default) and PathArc.Counterclockwise.
1613 The following QML can produce either of the two illustrated arcs below
1614 by changing the value of direction.
1617 \li \image declarative-arcdirection.png
1618 \li \snippet doc/src/snippets/qml/path/arcdirection.qml 0
1624 QQuickPathArc::ArcDirection QQuickPathArc::direction() const
1629 void QQuickPathArc::setDirection(ArcDirection direction)
1631 if (_direction == direction)
1634 _direction = direction;
1635 emit directionChanged();
1638 void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data)
1640 const QPointF &startPoint = path.currentPosition();
1641 const QPointF &endPoint = positionForCurve(data, startPoint);
1642 QQuickSvgParser::pathArc(path,
1647 _direction == Clockwise ? 1 : 0,
1650 startPoint.x(), startPoint.y());
1653 /****************************************************************************/
1656 \qmlclass PathSvg QQuickPathSvg
1657 \inqmlmodule QtQuick 2
1658 \ingroup qml-view-elements
1659 \brief The PathSvg defines a path using an SVG path data string.
1661 The following QML produces the path shown below:
1664 \li \image declarative-pathsvg.png
1668 startX: 50; startY: 50
1669 PathSvg { path: "L 150 50 L 100 150 z" }
1674 \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathCurve
1678 \qmlproperty string QtQuick2::PathSvg::path
1680 The SVG path data string specifying the path.
1682 See \l {http://www.w3.org/TR/SVG/paths.html#PathData}{W3C SVG Path Data}
1683 for more details on this format.
1686 QString QQuickPathSvg::path() const
1691 void QQuickPathSvg::setPath(const QString &path)
1700 void QQuickPathSvg::addToPath(QPainterPath &path, const QQuickPathData &)
1702 QQuickSvgParser::parsePathDataFast(_path, path);
1705 /****************************************************************************/
1708 \qmlclass PathPercent QQuickPathPercent
1709 \inqmlmodule QtQuick 2
1710 \ingroup qml-view-elements
1711 \brief The PathPercent manipulates the way a path is interpreted.
1713 PathPercent allows you to manipulate the spacing between items on a
1714 PathView's path. You can use it to bunch together items on part of
1715 the path, and spread them out on other parts of the path.
1717 The examples below show the normal distribution of items along a path
1718 compared to a distribution which places 50% of the items along the
1719 PathLine section of the path.
1722 \li \image declarative-nopercent.png
1728 startX: 20; startY: 0
1729 PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
1730 PathLine { x: 150; y: 80 }
1731 PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
1736 \li \image declarative-percent.png
1742 startX: 20; startY: 0
1743 PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
1744 PathPercent { value: 0.25 }
1745 PathLine { x: 150; y: 80 }
1746 PathPercent { value: 0.75 }
1747 PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
1748 PathPercent { value: 1 }
1758 \qmlproperty real QtQuick2::PathPercent::value
1759 The proportion of items that should be laid out up to this point.
1761 This value should always be higher than the last value specified
1762 by a PathPercent at a previous position in the Path.
1764 In the following example we have a Path made up of three PathLines.
1765 Normally, the items of the PathView would be laid out equally along
1766 this path, with an equal number of items per line segment. PathPercent
1767 allows us to specify that the first and third lines should each hold
1768 10% of the laid out items, while the second line should hold the remaining
1775 startX: 0; startY: 0
1776 PathLine { x:100; y: 0; }
1777 PathPercent { value: 0.1 }
1778 PathLine { x: 100; y: 100 }
1779 PathPercent { value: 0.9 }
1780 PathLine { x: 100; y: 0 }
1781 PathPercent { value: 1 }
1787 qreal QQuickPathPercent::value() const
1792 void QQuickPathPercent::setValue(qreal value)
1794 if (_value != value) {
1796 emit valueChanged();