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 \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.
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 if (!prevBez.isValid)
531 return p > .5 ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
532 forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
534 return p < prevBez.p ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
535 forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
538 QPointF QQuickPath::forwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
540 if (pathLength <= 0 || qIsNaN(pathLength))
541 return path.pointAtPercent(0); //expensive?
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;
551 //find which set we are in
552 qreal prevPercent = 0;
553 qreal prevOrigPercent = 0;
554 for (int ii = 0; ii < attributePoints.count(); ++ii) {
556 const AttributePoint &point = attributePoints.at(ii);
557 if (percent < point.percent || ii == attributePoints.count() - 1) {
558 qreal elementPercent = (percent - prevPercent);
560 qreal spc = prevOrigPercent + elementPercent * point.scale;
563 Q_ASSERT(!(currElement > lastElement));
564 Q_UNUSED(lastElement);
565 currBez = nextBezier(path, &currElement, &bezLength);
566 currLength += bezLength;
567 epc = currLength / pathLength;
569 prevBez.element = currElement;
570 prevBez.bezLength = bezLength;
571 prevBez.currLength = currLength;
572 prevBez.bezier = currBez;
574 prevBez.isValid = true;
576 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
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();
584 return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
586 prevOrigPercent = point.origpercent;
587 prevPercent = point.percent;
593 //ideally this should be merged with forwardsPointAt
594 QPointF QQuickPath::backwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
596 if (pathLength <= 0 || qIsNaN(pathLength))
597 return path.pointAtPercent(0);
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;
608 for (int ii = attributePoints.count() - 1; ii > 0; --ii) {
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);
615 qreal spc = prevPoint.origpercent + elementPercent * point.scale;
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;
627 prevBez.element = currElement;
628 prevBez.bezLength = bezLength;
629 prevBez.currLength = currLength;
630 prevBez.bezier = currBez;
632 prevBez.isValid = true;
634 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
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();
642 return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
649 QPointF QQuickPath::pointAt(qreal p) const
651 Q_D(const QQuickPath);
652 if (d->_pointCache.isEmpty()) {
654 if (d->_pointCache.isEmpty())
658 const int pointCacheSize = d->_pointCache.size();
659 qreal idxf = p*pointCacheSize;
660 int idx1 = qFloor(idxf);
661 qreal delta = idxf - idx1;
662 if (idx1 >= pointCacheSize)
663 idx1 = pointCacheSize - 1;
668 return d->_pointCache.at(idx1);
670 // interpolate between the two points.
671 int idx2 = qCeil(idxf);
672 if (idx2 >= pointCacheSize)
673 idx2 = pointCacheSize - 1;
677 QPointF p1 = d->_pointCache.at(idx1);
678 QPointF p2 = d->_pointCache.at(idx2);
679 QPointF pos = p1 * (1.0-delta) + p2 * delta;
684 qreal QQuickPath::attributeAt(const QString &name, qreal percent) const
686 Q_D(const QQuickPath);
687 if (percent < 0 || percent > 1)
690 for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
691 const AttributePoint &point = d->_attributePoints.at(ii);
693 if (point.percent == percent) {
694 return point.values.value(name);
695 } else if (point.percent > percent) {
697 ii?(d->_attributePoints.at(ii - 1).values.value(name)):0;
699 ii?(d->_attributePoints.at(ii - 1).percent):0;
700 qreal curValue = point.values.value(name);
701 qreal curPercent = point.percent;
703 return lastValue + (curValue - lastValue) * (percent - lastPercent) / (curPercent - lastPercent);
710 /****************************************************************************/
712 qreal QQuickCurve::x() const
714 return _x.isNull ? 0 : _x.value;
717 void QQuickCurve::setX(qreal x)
719 if (_x.isNull || _x != x) {
726 bool QQuickCurve::hasX()
731 qreal QQuickCurve::y() const
733 return _y.isNull ? 0 : _y.value;
736 void QQuickCurve::setY(qreal y)
738 if (_y.isNull || _y != y) {
745 bool QQuickCurve::hasY()
750 qreal QQuickCurve::relativeX() const
755 void QQuickCurve::setRelativeX(qreal x)
757 if (_relativeX.isNull || _relativeX != x) {
759 emit relativeXChanged();
764 bool QQuickCurve::hasRelativeX()
766 return _relativeX.isValid();
769 qreal QQuickCurve::relativeY() const
774 void QQuickCurve::setRelativeY(qreal y)
776 if (_relativeY.isNull || _relativeY != y) {
778 emit relativeYChanged();
783 bool QQuickCurve::hasRelativeY()
785 return _relativeY.isValid();
788 /****************************************************************************/
791 \qmlclass PathAttribute QQuickPathAttribute
792 \inqmlmodule QtQuick 2
793 \ingroup qml-view-elements
794 \brief The PathAttribute allows setting an attribute at a given position in a Path.
796 The PathAttribute object allows attributes consisting of a name and
797 a value to be specified for various points along a path. The
798 attributes are exposed to the delegate as
799 \l{qdeclarativeintroduction.html#attached-properties} {Attached Properties}.
800 The value of an attribute at any particular point along the path is interpolated
801 from the PathAttributes bounding that point.
803 The example below shows a path with the items scaled to 30% with
804 opacity 50% at the top of the path and scaled 100% with opacity
805 100% at the bottom. Note the use of the PathView.iconScale and
806 PathView.iconOpacity attached properties to set the scale and opacity
811 \o \image declarative-pathattribute.png
813 \snippet doc/src/snippets/qml/pathview/pathattributes.qml 0
814 (see the PathView documentation for the specification of ContactModel.qml
815 used for ContactModel above.)
823 \qmlproperty string QtQuick2::PathAttribute::name
824 This property holds the name of the attribute to change.
826 This attribute will be available to the delegate as PathView.<name>
828 Note that using an existing Item property name such as "opacity" as an
829 attribute is allowed. This is because path attributes add a new
830 \l{qdeclarativeintroduction.html#attached-properties} {Attached Property}
831 which in no way clashes with existing properties.
835 the name of the attribute to change.
838 QString QQuickPathAttribute::name() const
843 void QQuickPathAttribute::setName(const QString &name)
852 \qmlproperty real QtQuick2::PathAttribute::value
853 This property holds the value for the attribute.
855 The value specified can be used to influence the visual appearance
856 of an item along the path. For example, the following Path specifies
857 an attribute named \e itemRotation, which has the value \e 0 at the
858 beginning of the path, and the value 90 at the end of the path.
864 PathAttribute { name: "itemRotation"; value: 0 }
865 PathLine { x: 100; y: 100 }
866 PathAttribute { name: "itemRotation"; value: 90 }
870 In our delegate, we can then bind the \e rotation property to the
871 \l{qdeclarativeintroduction.html#attached-properties} {Attached Property}
872 \e PathView.itemRotation created for this attribute.
876 width: 10; height: 10
877 rotation: PathView.itemRotation
881 As each item is positioned along the path, it will be rotated accordingly:
882 an item at the beginning of the path with be not be rotated, an item at
883 the end of the path will be rotated 90 degrees, and an item mid-way along
884 the path will be rotated 45 degrees.
888 the new value of the attribute.
890 qreal QQuickPathAttribute::value() const
895 void QQuickPathAttribute::setValue(qreal value)
897 if (_value != value) {
904 /****************************************************************************/
907 \qmlclass PathLine QQuickPathLine
908 \inqmlmodule QtQuick 2
909 \ingroup qml-view-elements
910 \brief The PathLine defines a straight line.
912 The example below creates a path consisting of a straight line from
917 startX: 0; startY: 100
918 PathLine { x: 200; y: 100 }
922 \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
926 \qmlproperty real QtQuick2::PathLine::x
927 \qmlproperty real QtQuick2::PathLine::y
929 Defines the end point of the line.
931 \sa relativeX, relativeY
935 \qmlproperty real QtQuick2::PathLine::relativeX
936 \qmlproperty real QtQuick2::PathLine::relativeY
938 Defines the end point of the line relative to its start.
940 If both a relative and absolute end position are specified for a single axis, the relative
941 position will be used.
943 Relative and absolute positions can be mixed, for example it is valid to set a relative x
949 inline QPointF positionForCurve(const QQuickPathData &data, const QPointF &prevPoint)
951 QQuickCurve *curve = data.curves.at(data.index);
952 bool isEnd = data.index == data.curves.size() - 1;
953 return QPointF(curve->hasRelativeX() ? prevPoint.x() + curve->relativeX() : !isEnd || curve->hasX() ? curve->x() : data.endPoint.x(),
954 curve->hasRelativeY() ? prevPoint.y() + curve->relativeY() : !isEnd || curve->hasY() ? curve->y() : data.endPoint.y());
957 void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data)
959 path.lineTo(positionForCurve(data, path.currentPosition()));
962 /****************************************************************************/
965 \qmlclass PathQuad QQuickPathQuad
966 \inqmlmodule QtQuick 2
967 \ingroup qml-view-elements
968 \brief The PathQuad defines a quadratic Bezier curve with a control point.
970 The following QML produces the path shown below:
973 \o \image declarative-pathquad.png
978 PathQuad { x: 200; y: 0; controlX: 100; controlY: 150 }
983 \sa Path, PathCubic, PathLine, PathArc, PathCurve, PathSvg
987 \qmlproperty real QtQuick2::PathQuad::x
988 \qmlproperty real QtQuick2::PathQuad::y
990 Defines the end point of the curve.
992 \sa relativeX, relativeY
996 \qmlproperty real QtQuick2::PathQuad::relativeX
997 \qmlproperty real QtQuick2::PathQuad::relativeY
999 Defines the end point of the curve relative to its start.
1001 If both a relative and absolute end position are specified for a single axis, the relative
1002 position will be used.
1004 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1011 \qmlproperty real QtQuick2::PathQuad::controlX
1012 \qmlproperty real QtQuick2::PathQuad::controlY
1014 Defines the position of the control point.
1018 the x position of the control point.
1020 qreal QQuickPathQuad::controlX() const
1025 void QQuickPathQuad::setControlX(qreal x)
1027 if (_controlX != x) {
1029 emit controlXChanged();
1036 the y position of the control point.
1038 qreal QQuickPathQuad::controlY() const
1043 void QQuickPathQuad::setControlY(qreal y)
1045 if (_controlY != y) {
1047 emit controlYChanged();
1053 \qmlproperty real QtQuick2::PathCubic::relativeControlX
1054 \qmlproperty real QtQuick2::PathCubic::relativeControlY
1056 Defines the position of the control point relative to the curve's start.
1058 If both a relative and absolute control position are specified for a single axis, the relative
1059 position will be used.
1061 Relative and absolute positions can be mixed, for example it is valid to set a relative control x
1062 and an absolute control y.
1064 \sa controlX, controlY
1067 qreal QQuickPathQuad::relativeControlX() const
1069 return _relativeControlX;
1072 void QQuickPathQuad::setRelativeControlX(qreal x)
1074 if (_relativeControlX.isNull || _relativeControlX != x) {
1075 _relativeControlX = x;
1076 emit relativeControlXChanged();
1081 bool QQuickPathQuad::hasRelativeControlX()
1083 return _relativeControlX.isValid();
1086 qreal QQuickPathQuad::relativeControlY() const
1088 return _relativeControlY;
1091 void QQuickPathQuad::setRelativeControlY(qreal y)
1093 if (_relativeControlY.isNull || _relativeControlY != y) {
1094 _relativeControlY = y;
1095 emit relativeControlYChanged();
1100 bool QQuickPathQuad::hasRelativeControlY()
1102 return _relativeControlY.isValid();
1105 void QQuickPathQuad::addToPath(QPainterPath &path, const QQuickPathData &data)
1107 const QPointF &prevPoint = path.currentPosition();
1108 QPointF controlPoint(hasRelativeControlX() ? prevPoint.x() + relativeControlX() : controlX(),
1109 hasRelativeControlY() ? prevPoint.y() + relativeControlY() : controlY());
1110 path.quadTo(controlPoint, positionForCurve(data, path.currentPosition()));
1113 /****************************************************************************/
1116 \qmlclass PathCubic QQuickPathCubic
1117 \inqmlmodule QtQuick 2
1118 \ingroup qml-view-elements
1119 \brief The PathCubic defines a cubic Bezier curve with two control points.
1121 The following QML produces the path shown below:
1124 \o \image declarative-pathcubic.png
1128 startX: 20; startY: 0
1131 control1X: -10; control1Y: 90
1132 control2X: 210; control2Y: 90
1138 \sa Path, PathQuad, PathLine, PathArc, PathCurve, PathSvg
1142 \qmlproperty real QtQuick2::PathCubic::x
1143 \qmlproperty real QtQuick2::PathCubic::y
1145 Defines the end point of the curve.
1147 \sa relativeX, relativeY
1151 \qmlproperty real QtQuick2::PathCubic::relativeX
1152 \qmlproperty real QtQuick2::PathCubic::relativeY
1154 Defines the end point of the curve relative to its start.
1156 If both a relative and absolute end position are specified for a single axis, the relative
1157 position will be used.
1159 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1166 \qmlproperty real QtQuick2::PathCubic::control1X
1167 \qmlproperty real QtQuick2::PathCubic::control1Y
1169 Defines the position of the first control point.
1171 qreal QQuickPathCubic::control1X() const
1176 void QQuickPathCubic::setControl1X(qreal x)
1178 if (_control1X != x) {
1180 emit control1XChanged();
1185 qreal QQuickPathCubic::control1Y() const
1190 void QQuickPathCubic::setControl1Y(qreal y)
1192 if (_control1Y != y) {
1194 emit control1YChanged();
1200 \qmlproperty real QtQuick2::PathCubic::control2X
1201 \qmlproperty real QtQuick2::PathCubic::control2Y
1203 Defines the position of the second control point.
1205 qreal QQuickPathCubic::control2X() const
1210 void QQuickPathCubic::setControl2X(qreal x)
1212 if (_control2X != x) {
1214 emit control2XChanged();
1219 qreal QQuickPathCubic::control2Y() const
1224 void QQuickPathCubic::setControl2Y(qreal y)
1226 if (_control2Y != y) {
1228 emit control2YChanged();
1234 \qmlproperty real QtQuick2::PathCubic::relativeControl1X
1235 \qmlproperty real QtQuick2::PathCubic::relativeControl1Y
1236 \qmlproperty real QtQuick2::PathCubic::relativeControl2X
1237 \qmlproperty real QtQuick2::PathCubic::relativeControl2Y
1239 Defines the positions of the control points relative to the curve's start.
1241 If both a relative and absolute control position are specified for a control point's axis, the relative
1242 position will be used.
1244 Relative and absolute positions can be mixed, for example it is valid to set a relative control1 x
1245 and an absolute control1 y.
1247 \sa control1X, control1Y, control2X, control2Y
1250 qreal QQuickPathCubic::relativeControl1X() const
1252 return _relativeControl1X;
1255 void QQuickPathCubic::setRelativeControl1X(qreal x)
1257 if (_relativeControl1X.isNull || _relativeControl1X != x) {
1258 _relativeControl1X = x;
1259 emit relativeControl1XChanged();
1264 bool QQuickPathCubic::hasRelativeControl1X()
1266 return _relativeControl1X.isValid();
1269 qreal QQuickPathCubic::relativeControl1Y() const
1271 return _relativeControl1Y;
1274 void QQuickPathCubic::setRelativeControl1Y(qreal y)
1276 if (_relativeControl1Y.isNull || _relativeControl1Y != y) {
1277 _relativeControl1Y = y;
1278 emit relativeControl1YChanged();
1283 bool QQuickPathCubic::hasRelativeControl1Y()
1285 return _relativeControl1Y.isValid();
1288 qreal QQuickPathCubic::relativeControl2X() const
1290 return _relativeControl2X;
1293 void QQuickPathCubic::setRelativeControl2X(qreal x)
1295 if (_relativeControl2X.isNull || _relativeControl2X != x) {
1296 _relativeControl2X = x;
1297 emit relativeControl2XChanged();
1302 bool QQuickPathCubic::hasRelativeControl2X()
1304 return _relativeControl2X.isValid();
1307 qreal QQuickPathCubic::relativeControl2Y() const
1309 return _relativeControl2Y;
1312 void QQuickPathCubic::setRelativeControl2Y(qreal y)
1314 if (_relativeControl2Y.isNull || _relativeControl2Y != y) {
1315 _relativeControl2Y = y;
1316 emit relativeControl2YChanged();
1321 bool QQuickPathCubic::hasRelativeControl2Y()
1323 return _relativeControl2Y.isValid();
1326 void QQuickPathCubic::addToPath(QPainterPath &path, const QQuickPathData &data)
1328 const QPointF &prevPoint = path.currentPosition();
1329 QPointF controlPoint1(hasRelativeControl1X() ? prevPoint.x() + relativeControl1X() : control1X(),
1330 hasRelativeControl1Y() ? prevPoint.y() + relativeControl1Y() : control1Y());
1331 QPointF controlPoint2(hasRelativeControl2X() ? prevPoint.x() + relativeControl2X() : control2X(),
1332 hasRelativeControl2Y() ? prevPoint.y() + relativeControl2Y() : control2Y());
1333 path.cubicTo(controlPoint1, controlPoint2, positionForCurve(data, path.currentPosition()));
1336 /****************************************************************************/
1339 \qmlclass PathCurve QQuickPathCurve
1340 \inqmlmodule QtQuick 2
1341 \ingroup qml-view-elements
1342 \brief The PathCurve defines a point on a Catmull-Rom curve.
1344 PathCurve provides an easy way to specify a curve passing directly through a set of points.
1345 Typically multiple PathCurves are used in a series, as the following example demonstrates:
1347 \snippet doc/src/snippets/qml/path/basiccurve.qml 0
1349 This example produces the following path (with the starting point and PathCurve points
1350 highlighted in red):
1352 \image declarative-pathcurve.png
1354 \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathSvg
1358 \qmlproperty real QtQuick2::PathCurve::x
1359 \qmlproperty real QtQuick2::PathCurve::y
1361 Defines the end point of the curve.
1363 \sa relativeX, relativeY
1367 \qmlproperty real QtQuick2::PathCurve::relativeX
1368 \qmlproperty real QtQuick2::PathCurve::relativeY
1370 Defines the end point of the curve relative to its start.
1372 If both a relative and absolute end position are specified for a single axis, the relative
1373 position will be used.
1375 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1381 inline QPointF previousPathPosition(const QPainterPath &path)
1383 int count = path.elementCount();
1387 int index = path.elementAt(count-1).type == QPainterPath::CurveToDataElement ? count - 4 : count - 2;
1388 return index > -1 ? QPointF(path.elementAt(index)) : path.pointAtPercent(0);
1391 void QQuickPathCatmullRomCurve::addToPath(QPainterPath &path, const QQuickPathData &data)
1393 //here we convert catmull-rom spline to bezier for use in QPainterPath.
1394 //basic conversion algorithm:
1395 // catmull-rom points * inverse bezier matrix * catmull-rom matrix = bezier points
1396 //each point in the catmull-rom spline produces a bezier endpoint + 2 control points
1397 //calculations for each point use a moving window of 4 points
1398 // (previous 2 points + current point + next point)
1399 QPointF prevFar, prev, point, next;
1401 //get previous points
1402 int index = data.index - 1;
1403 QQuickCurve *curve = index == -1 ? 0 : data.curves.at(index);
1404 if (qobject_cast<QQuickPathCatmullRomCurve*>(curve)) {
1405 prev = path.currentPosition();
1406 prevFar = previousPathPosition(path);
1408 prev = path.currentPosition();
1409 bool prevFarSet = false;
1410 if (index == -1 && data.curves.count() > 1) {
1411 if (qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(data.curves.count()-1))) {
1412 //TODO: profile and optimize
1414 QQuickPathData loopData;
1415 loopData.endPoint = data.endPoint;
1416 loopData.curves = data.curves;
1417 for (int i = data.index; i < data.curves.count(); ++i) {
1419 pos = positionForCurve(loopData, pos);
1420 if (i == data.curves.count()-2)
1423 if (pos == QPointF(path.elementAt(0))) {
1424 //this is a closed path starting and ending with catmull-rom segments.
1425 //we try to smooth the join point
1435 point = positionForCurve(data, path.currentPosition());
1438 index = data.index + 1;
1439 if (index < data.curves.count() && qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(index))) {
1440 QQuickPathData nextData;
1441 nextData.index = index;
1442 nextData.endPoint = data.endPoint;
1443 nextData.curves = data.curves;
1444 next = positionForCurve(nextData, point);
1446 if (point == QPointF(path.elementAt(0)) && qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(0))) {
1447 //this is a closed path starting and ending with catmull-rom segments.
1448 //we try to smooth the join point
1449 next = QPointF(path.elementAt(3)); //the first catmull-rom point
1455 full conversion matrix (inverse bezier * catmull-rom):
1456 0.000, 1.000, 0.000, 0.000,
1457 -0.167, 1.000, 0.167, 0.000,
1458 0.000, 0.167, 1.000, -0.167,
1459 0.000, 0.000, 1.000, 0.000
1461 conversion doesn't require full matrix multiplication,
1462 so below we simplify
1464 QPointF control1(prevFar.x() * qreal(-0.167) +
1466 point.x() * qreal(0.167),
1467 prevFar.y() * qreal(-0.167) +
1469 point.y() * qreal(0.167));
1471 QPointF control2(prev.x() * qreal(0.167) +
1473 next.x() * qreal(-0.167),
1474 prev.y() * qreal(0.167) +
1476 next.y() * qreal(-0.167));
1478 path.cubicTo(control1, control2, point);
1481 /****************************************************************************/
1484 \qmlclass PathArc QQuickPathArc
1485 \inqmlmodule QtQuick 2
1486 \ingroup qml-view-elements
1487 \brief The PathArc defines an arc with the given radius.
1489 PathArc provides a simple way of specifying an arc that ends at a given position
1490 and uses the specified radius. It is modeled after the SVG elliptical arc command.
1492 The following QML produces the path shown below:
1495 \o \image declarative-patharc.png
1496 \o \snippet doc/src/snippets/qml/path/basicarc.qml 0
1499 Note that a single PathArc cannot be used to specify a circle. Instead, you can
1500 use two PathArc elements, each specifying half of the circle.
1502 \sa Path, PathLine, PathQuad, PathCubic, PathCurve, PathSvg
1506 \qmlproperty real QtQuick2::PathArc::x
1507 \qmlproperty real QtQuick2::PathArc::y
1509 Defines the end point of the arc.
1511 \sa relativeX, relativeY
1515 \qmlproperty real QtQuick2::PathArc::relativeX
1516 \qmlproperty real QtQuick2::PathArc::relativeY
1518 Defines the end point of the arc relative to its start.
1520 If both a relative and absolute end position are specified for a single axis, the relative
1521 position will be used.
1523 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1530 \qmlproperty real QtQuick2::PathArc::radiusX
1531 \qmlproperty real QtQuick2::PathArc::radiusY
1533 Defines the radius of the arc.
1535 The following QML demonstrates how different radius values can be used to change
1536 the shape of the arc:
1539 \o \image declarative-arcradius.png
1540 \o \snippet doc/src/snippets/qml/path/arcradius.qml 0
1544 qreal QQuickPathArc::radiusX() const
1549 void QQuickPathArc::setRadiusX(qreal radius)
1551 if (_radiusX == radius)
1555 emit radiusXChanged();
1558 qreal QQuickPathArc::radiusY() const
1563 void QQuickPathArc::setRadiusY(qreal radius)
1565 if (_radiusY == radius)
1569 emit radiusYChanged();
1573 \qmlproperty bool QtQuick2::PathArc::useLargeArc
1574 Whether to use a large arc as defined by the arc points.
1576 Given fixed start and end positions, radius, and direction,
1577 there are two possible arcs that can fit the data. useLargeArc
1578 is used to distinguish between these. For example, the following
1579 QML can produce either of the two illustrated arcs below by
1580 changing the value of useLargeArc.
1584 \o \image declarative-largearc.png
1585 \o \snippet doc/src/snippets/qml/path/largearc.qml 0
1588 The default value is false.
1591 bool QQuickPathArc::useLargeArc() const
1593 return _useLargeArc;
1596 void QQuickPathArc::setUseLargeArc(bool largeArc)
1598 if (_useLargeArc == largeArc)
1601 _useLargeArc = largeArc;
1602 emit useLargeArcChanged();
1606 \qmlproperty enum QtQuick2::PathArc::direction
1608 Defines the direction of the arc. Possible values are
1609 PathArc.Clockwise (default) and PathArc.Counterclockwise.
1611 The following QML can produce either of the two illustrated arcs below
1612 by changing the value of direction.
1615 \o \image declarative-arcdirection.png
1616 \o \snippet doc/src/snippets/qml/path/arcdirection.qml 0
1622 QQuickPathArc::ArcDirection QQuickPathArc::direction() const
1627 void QQuickPathArc::setDirection(ArcDirection direction)
1629 if (_direction == direction)
1632 _direction = direction;
1633 emit directionChanged();
1636 void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data)
1638 const QPointF &startPoint = path.currentPosition();
1639 const QPointF &endPoint = positionForCurve(data, startPoint);
1640 QQuickSvgParser::pathArc(path,
1645 _direction == Clockwise ? 1 : 0,
1648 startPoint.x(), startPoint.y());
1651 /****************************************************************************/
1654 \qmlclass PathSvg QQuickPathSvg
1655 \inqmlmodule QtQuick 2
1656 \ingroup qml-view-elements
1657 \brief The PathSvg defines a path using an SVG path data string.
1659 The following QML produces the path shown below:
1662 \o \image declarative-pathsvg.png
1666 startX: 50; startY: 50
1667 PathSvg { path: "L 150 50 L 100 150 z" }
1672 \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathCurve
1676 \qmlproperty string QtQuick2::PathSvg::path
1678 The SVG path data string specifying the path.
1680 See \l {http://www.w3.org/TR/SVG/paths.html#PathData}{W3C SVG Path Data}
1681 for more details on this format.
1684 QString QQuickPathSvg::path() const
1689 void QQuickPathSvg::setPath(const QString &path)
1698 void QQuickPathSvg::addToPath(QPainterPath &path, const QQuickPathData &)
1700 QQuickSvgParser::parsePathDataFast(_path, path);
1703 /****************************************************************************/
1706 \qmlclass PathPercent QQuickPathPercent
1707 \inqmlmodule QtQuick 2
1708 \ingroup qml-view-elements
1709 \brief The PathPercent manipulates the way a path is interpreted.
1711 PathPercent allows you to manipulate the spacing between items on a
1712 PathView's path. You can use it to bunch together items on part of
1713 the path, and spread them out on other parts of the path.
1715 The examples below show the normal distribution of items along a path
1716 compared to a distribution which places 50% of the items along the
1717 PathLine section of the path.
1720 \o \image declarative-nopercent.png
1726 startX: 20; startY: 0
1727 PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
1728 PathLine { x: 150; y: 80 }
1729 PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
1734 \o \image declarative-percent.png
1740 startX: 20; startY: 0
1741 PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
1742 PathPercent { value: 0.25 }
1743 PathLine { x: 150; y: 80 }
1744 PathPercent { value: 0.75 }
1745 PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
1746 PathPercent { value: 1 }
1756 \qmlproperty real QtQuick2::PathPercent::value
1757 The proportion of items that should be laid out up to this point.
1759 This value should always be higher than the last value specified
1760 by a PathPercent at a previous position in the Path.
1762 In the following example we have a Path made up of three PathLines.
1763 Normally, the items of the PathView would be laid out equally along
1764 this path, with an equal number of items per line segment. PathPercent
1765 allows us to specify that the first and third lines should each hold
1766 10% of the laid out items, while the second line should hold the remaining
1773 startX: 0; startY: 0
1774 PathLine { x:100; y: 0; }
1775 PathPercent { value: 0.1 }
1776 PathLine { x: 100; y: 100 }
1777 PathPercent { value: 0.9 }
1778 PathLine { x: 100; y: 0 }
1779 PathPercent { value: 1 }
1785 qreal QQuickPathPercent::value() const
1790 void QQuickPathPercent::setValue(qreal value)
1792 if (_value != value) {
1794 emit valueChanged();