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 QtDeclarative 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 "qdeclarativepath_p.h"
43 #include "qdeclarativepath_p_p.h"
44 #include "qdeclarativesvgparser_p.h"
49 #include <private/qbezier_p.h>
50 #include <QtCore/qmath.h>
51 #include <QtCore/qnumeric.h>
56 \qmlclass PathElement QDeclarativePathElement
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 QDeclarativePath
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 QDeclarativePath::QDeclarativePath(QObject *parent)
85 : QObject(*(new QDeclarativePathPrivate), parent)
89 QDeclarativePath::~QDeclarativePath()
94 \qmlproperty real QtQuick2::Path::startX
95 \qmlproperty real QtQuick2::Path::startY
96 These properties hold the starting position of the path.
98 qreal QDeclarativePath::startX() const
100 Q_D(const QDeclarativePath);
101 return d->startX.isNull ? 0 : d->startX.value;
104 void QDeclarativePath::setStartX(qreal x)
106 Q_D(QDeclarativePath);
107 if (d->startX.isValid() && qFuzzyCompare(x, d->startX))
110 emit startXChanged();
114 bool QDeclarativePath::hasStartX() const
116 Q_D(const QDeclarativePath);
117 return d->startX.isValid();
120 qreal QDeclarativePath::startY() const
122 Q_D(const QDeclarativePath);
123 return d->startY.isNull ? 0 : d->startY.value;
126 void QDeclarativePath::setStartY(qreal y)
128 Q_D(QDeclarativePath);
129 if (d->startY.isValid() && qFuzzyCompare(y, d->startY))
132 emit startYChanged();
136 bool QDeclarativePath::hasStartY() const
138 Q_D(const QDeclarativePath);
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 QDeclarativePath::isClosed() const
148 Q_D(const QDeclarativePath);
152 bool QDeclarativePath::hasEnd() const
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()))
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/declarative/pathview/pathattributes.qml 2
187 QDeclarativeListProperty<QDeclarativePathElement> QDeclarativePath::pathElements()
189 Q_D(QDeclarativePath);
190 return QDeclarativeListProperty<QDeclarativePathElement>(this, d->_pathElements);
193 void QDeclarativePath::interpolate(int idx, const QString &name, qreal value)
195 Q_D(QDeclarativePath);
196 interpolate(d->_attributePoints, idx, name, value);
199 void QDeclarativePath::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 QDeclarativePath::endpoint(const QString &name)
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);
246 void QDeclarativePath::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 QDeclarativePath::processPath()
266 Q_D(QDeclarativePath);
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 QDeclarativePath::createPath(const QPointF &startPoint, const QPointF &endPoint, const QStringList &attributes, qreal &pathLength, QList<AttributePoint> &attributePoints, bool *closed)
281 Q_D(QDeclarativePath);
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 (QDeclarativePathElement *pathElement, d->_pathElements) {
303 if (QDeclarativeCurve *curve = qobject_cast<QDeclarativeCurve *>(pathElement)) {
304 QDeclarativePathData data;
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 (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());
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 QDeclarativePathPercent
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 QDeclarativePath::classBegin()
370 Q_D(QDeclarativePath);
371 d->componentComplete = false;
374 void QDeclarativePath::componentComplete()
376 Q_D(QDeclarativePath);
378 d->componentComplete = true;
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());
389 d->_attributes = attrs.toList();
393 foreach (QDeclarativePathElement *pathElement, d->_pathElements)
394 connect(pathElement, SIGNAL(changed()), this, SLOT(processPath()));
397 QPainterPath QDeclarativePath::path() const
399 Q_D(const QDeclarativePath);
403 QStringList QDeclarativePath::attributes() const
405 Q_D(const QDeclarativePath);
406 if (!d->componentComplete) {
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());
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 QDeclarativePath::createPointCache() const
466 Q_D(const QDeclarativePath);
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 QDeclarativePath::invalidateSequentialHistory() const
518 Q_D(const QDeclarativePath);
519 d->prevBez.isValid = false;
522 QPointF QDeclarativePath::sequentialPointAt(qreal p, qreal *angle) const
524 Q_D(const QDeclarativePath);
525 return sequentialPointAt(d->_path, d->pathLength, d->_attributePoints, d->prevBez, p, angle);
528 QPointF QDeclarativePath::sequentialPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &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 QDeclarativePath::forwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &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 QDeclarativePath::backwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &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 QDeclarativePath::pointAt(qreal p) const
651 Q_D(const QDeclarativePath);
652 if (d->_pointCache.isEmpty()) {
654 if (d->_pointCache.isEmpty())
657 int idx = qRound(p*d->_pointCache.size());
658 if (idx >= d->_pointCache.size())
659 idx = d->_pointCache.size() - 1;
662 return d->_pointCache.at(idx);
665 qreal QDeclarativePath::attributeAt(const QString &name, qreal percent) const
667 Q_D(const QDeclarativePath);
668 if (percent < 0 || percent > 1)
671 for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
672 const AttributePoint &point = d->_attributePoints.at(ii);
674 if (point.percent == percent) {
675 return point.values.value(name);
676 } else if (point.percent > percent) {
678 ii?(d->_attributePoints.at(ii - 1).values.value(name)):0;
680 ii?(d->_attributePoints.at(ii - 1).percent):0;
681 qreal curValue = point.values.value(name);
682 qreal curPercent = point.percent;
684 return lastValue + (curValue - lastValue) * (percent - lastPercent) / (curPercent - lastPercent);
691 /****************************************************************************/
693 qreal QDeclarativeCurve::x() const
695 return _x.isNull ? 0 : _x.value;
698 void QDeclarativeCurve::setX(qreal x)
700 if (_x.isNull || _x != x) {
707 bool QDeclarativeCurve::hasX()
712 qreal QDeclarativeCurve::y() const
714 return _y.isNull ? 0 : _y.value;
717 void QDeclarativeCurve::setY(qreal y)
719 if (_y.isNull || _y != y) {
726 bool QDeclarativeCurve::hasY()
731 qreal QDeclarativeCurve::relativeX() const
736 void QDeclarativeCurve::setRelativeX(qreal x)
738 if (_relativeX.isNull || _relativeX != x) {
740 emit relativeXChanged();
745 bool QDeclarativeCurve::hasRelativeX()
747 return _relativeX.isValid();
750 qreal QDeclarativeCurve::relativeY() const
755 void QDeclarativeCurve::setRelativeY(qreal y)
757 if (_relativeY.isNull || _relativeY != y) {
759 emit relativeYChanged();
764 bool QDeclarativeCurve::hasRelativeY()
766 return _relativeY.isValid();
769 /****************************************************************************/
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.
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.
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
792 \o \image declarative-pathattribute.png
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.)
804 \qmlproperty string QtQuick2::PathAttribute::name
805 This property holds the name of the attribute to change.
807 This attribute will be available to the delegate as PathView.<name>
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.
816 the name of the attribute to change.
819 QString QDeclarativePathAttribute::name() const
824 void QDeclarativePathAttribute::setName(const QString &name)
833 \qmlproperty real QtQuick2::PathAttribute::value
834 This property holds the value for the attribute.
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.
845 PathAttribute { name: "itemRotation"; value: 0 }
846 PathLine { x: 100; y: 100 }
847 PathAttribute { name: "itemRotation"; value: 90 }
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.
857 width: 10; height: 10
858 rotation: PathView.itemRotation
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.
869 the new value of the attribute.
871 qreal QDeclarativePathAttribute::value() const
876 void QDeclarativePathAttribute::setValue(qreal value)
878 if (_value != value) {
885 /****************************************************************************/
888 \qmlclass PathLine QDeclarativePathLine
889 \inqmlmodule QtQuick 2
890 \ingroup qml-view-elements
891 \brief The PathLine defines a straight line.
893 The example below creates a path consisting of a straight line from
898 startX: 0; startY: 100
899 PathLine { x: 200; y: 100 }
903 \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
907 \qmlproperty real QtQuick2::PathLine::x
908 \qmlproperty real QtQuick2::PathLine::y
910 Defines the end point of the line.
912 \sa relativeX, relativeY
916 \qmlproperty real QtQuick2::PathLine::relativeX
917 \qmlproperty real QtQuick2::PathLine::relativeY
919 Defines the end point of the line relative to its start.
921 If both a relative and absolute end position are specified for a single axis, the relative
922 position will be used.
924 Relative and absolute positions can be mixed, for example it is valid to set a relative x
930 inline QPointF positionForCurve(const QDeclarativePathData &data, const QPointF &prevPoint)
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());
938 void QDeclarativePathLine::addToPath(QPainterPath &path, const QDeclarativePathData &data)
940 path.lineTo(positionForCurve(data, path.currentPosition()));
943 /****************************************************************************/
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.
951 The following QML produces the path shown below:
954 \o \image declarative-pathquad.png
959 PathQuad { x: 200; y: 0; controlX: 100; controlY: 150 }
964 \sa Path, PathCubic, PathLine, PathArc, PathCurve, PathSvg
968 \qmlproperty real QtQuick2::PathQuad::x
969 \qmlproperty real QtQuick2::PathQuad::y
971 Defines the end point of the curve.
973 \sa relativeX, relativeY
977 \qmlproperty real QtQuick2::PathQuad::relativeX
978 \qmlproperty real QtQuick2::PathQuad::relativeY
980 Defines the end point of the curve relative to its start.
982 If both a relative and absolute end position are specified for a single axis, the relative
983 position will be used.
985 Relative and absolute positions can be mixed, for example it is valid to set a relative x
992 \qmlproperty real QtQuick2::PathQuad::controlX
993 \qmlproperty real QtQuick2::PathQuad::controlY
995 Defines the position of the control point.
999 the x position of the control point.
1001 qreal QDeclarativePathQuad::controlX() const
1006 void QDeclarativePathQuad::setControlX(qreal x)
1008 if (_controlX != x) {
1010 emit controlXChanged();
1017 the y position of the control point.
1019 qreal QDeclarativePathQuad::controlY() const
1024 void QDeclarativePathQuad::setControlY(qreal y)
1026 if (_controlY != y) {
1028 emit controlYChanged();
1034 \qmlproperty real QtQuick2::PathCubic::relativeControlX
1035 \qmlproperty real QtQuick2::PathCubic::relativeControlY
1037 Defines the position of the control point relative to the curve's start.
1039 If both a relative and absolute control position are specified for a single axis, the relative
1040 position will be used.
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.
1045 \sa controlX, controlY
1048 qreal QDeclarativePathQuad::relativeControlX() const
1050 return _relativeControlX;
1053 void QDeclarativePathQuad::setRelativeControlX(qreal x)
1055 if (_relativeControlX.isNull || _relativeControlX != x) {
1056 _relativeControlX = x;
1057 emit relativeControlXChanged();
1062 bool QDeclarativePathQuad::hasRelativeControlX()
1064 return _relativeControlX.isValid();
1067 qreal QDeclarativePathQuad::relativeControlY() const
1069 return _relativeControlY;
1072 void QDeclarativePathQuad::setRelativeControlY(qreal y)
1074 if (_relativeControlY.isNull || _relativeControlY != y) {
1075 _relativeControlY = y;
1076 emit relativeControlYChanged();
1081 bool QDeclarativePathQuad::hasRelativeControlY()
1083 return _relativeControlY.isValid();
1086 void QDeclarativePathQuad::addToPath(QPainterPath &path, const QDeclarativePathData &data)
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()));
1094 /****************************************************************************/
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.
1102 The following QML produces the path shown below:
1105 \o \image declarative-pathcubic.png
1109 startX: 20; startY: 0
1112 control1X: -10; control1Y: 90
1113 control2X: 210; control2Y: 90
1119 \sa Path, PathQuad, PathLine, PathArc, PathCurve, PathSvg
1123 \qmlproperty real QtQuick2::PathCubic::x
1124 \qmlproperty real QtQuick2::PathCubic::y
1126 Defines the end point of the curve.
1128 \sa relativeX, relativeY
1132 \qmlproperty real QtQuick2::PathCubic::relativeX
1133 \qmlproperty real QtQuick2::PathCubic::relativeY
1135 Defines the end point of the curve relative to its start.
1137 If both a relative and absolute end position are specified for a single axis, the relative
1138 position will be used.
1140 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1147 \qmlproperty real QtQuick2::PathCubic::control1X
1148 \qmlproperty real QtQuick2::PathCubic::control1Y
1150 Defines the position of the first control point.
1152 qreal QDeclarativePathCubic::control1X() const
1157 void QDeclarativePathCubic::setControl1X(qreal x)
1159 if (_control1X != x) {
1161 emit control1XChanged();
1166 qreal QDeclarativePathCubic::control1Y() const
1171 void QDeclarativePathCubic::setControl1Y(qreal y)
1173 if (_control1Y != y) {
1175 emit control1YChanged();
1181 \qmlproperty real QtQuick2::PathCubic::control2X
1182 \qmlproperty real QtQuick2::PathCubic::control2Y
1184 Defines the position of the second control point.
1186 qreal QDeclarativePathCubic::control2X() const
1191 void QDeclarativePathCubic::setControl2X(qreal x)
1193 if (_control2X != x) {
1195 emit control2XChanged();
1200 qreal QDeclarativePathCubic::control2Y() const
1205 void QDeclarativePathCubic::setControl2Y(qreal y)
1207 if (_control2Y != y) {
1209 emit control2YChanged();
1215 \qmlproperty real QtQuick2::PathCubic::relativeControl1X
1216 \qmlproperty real QtQuick2::PathCubic::relativeControl1Y
1217 \qmlproperty real QtQuick2::PathCubic::relativeControl2X
1218 \qmlproperty real QtQuick2::PathCubic::relativeControl2Y
1220 Defines the positions of the control points relative to the curve's start.
1222 If both a relative and absolute control position are specified for a control point's axis, the relative
1223 position will be used.
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.
1228 \sa control1X, control1Y, control2X, control2Y
1231 qreal QDeclarativePathCubic::relativeControl1X() const
1233 return _relativeControl1X;
1236 void QDeclarativePathCubic::setRelativeControl1X(qreal x)
1238 if (_relativeControl1X.isNull || _relativeControl1X != x) {
1239 _relativeControl1X = x;
1240 emit relativeControl1XChanged();
1245 bool QDeclarativePathCubic::hasRelativeControl1X()
1247 return _relativeControl1X.isValid();
1250 qreal QDeclarativePathCubic::relativeControl1Y() const
1252 return _relativeControl1Y;
1255 void QDeclarativePathCubic::setRelativeControl1Y(qreal y)
1257 if (_relativeControl1Y.isNull || _relativeControl1Y != y) {
1258 _relativeControl1Y = y;
1259 emit relativeControl1YChanged();
1264 bool QDeclarativePathCubic::hasRelativeControl1Y()
1266 return _relativeControl1Y.isValid();
1269 qreal QDeclarativePathCubic::relativeControl2X() const
1271 return _relativeControl2X;
1274 void QDeclarativePathCubic::setRelativeControl2X(qreal x)
1276 if (_relativeControl2X.isNull || _relativeControl2X != x) {
1277 _relativeControl2X = x;
1278 emit relativeControl2XChanged();
1283 bool QDeclarativePathCubic::hasRelativeControl2X()
1285 return _relativeControl2X.isValid();
1288 qreal QDeclarativePathCubic::relativeControl2Y() const
1290 return _relativeControl2Y;
1293 void QDeclarativePathCubic::setRelativeControl2Y(qreal y)
1295 if (_relativeControl2Y.isNull || _relativeControl2Y != y) {
1296 _relativeControl2Y = y;
1297 emit relativeControl2YChanged();
1302 bool QDeclarativePathCubic::hasRelativeControl2Y()
1304 return _relativeControl2Y.isValid();
1307 void QDeclarativePathCubic::addToPath(QPainterPath &path, const QDeclarativePathData &data)
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()));
1317 /****************************************************************************/
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.
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:
1328 \snippet doc/src/snippets/declarative/path/basiccurve.qml 0
1330 This example produces the following path (with the starting point and PathCurve points
1331 highlighted in red):
1333 \image declarative-pathcurve.png
1335 \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathSvg
1339 \qmlproperty real QtQuick2::PathCurve::x
1340 \qmlproperty real QtQuick2::PathCurve::y
1342 Defines the end point of the curve.
1344 \sa relativeX, relativeY
1348 \qmlproperty real QtQuick2::PathCurve::relativeX
1349 \qmlproperty real QtQuick2::PathCurve::relativeY
1351 Defines the end point of the curve relative to its start.
1353 If both a relative and absolute end position are specified for a single axis, the relative
1354 position will be used.
1356 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1362 inline QPointF previousPathPosition(const QPainterPath &path)
1364 int count = path.elementCount();
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);
1372 void QDeclarativePathCatmullRomCurve::addToPath(QPainterPath &path, const QDeclarativePathData &data)
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;
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);
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
1395 QDeclarativePathData loopData;
1396 loopData.endPoint = data.endPoint;
1397 loopData.curves = data.curves;
1398 for (int i = data.index; i < data.curves.count(); ++i) {
1400 pos = positionForCurve(loopData, pos);
1401 if (i == data.curves.count()-2)
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
1416 point = positionForCurve(data, path.currentPosition());
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);
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
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
1442 conversion doesn't require full matrix multiplication,
1443 so below we simplify
1445 QPointF control1(prevFar.x() * qreal(-0.167) +
1447 point.x() * qreal(0.167),
1448 prevFar.y() * qreal(-0.167) +
1450 point.y() * qreal(0.167));
1452 QPointF control2(prev.x() * qreal(0.167) +
1454 next.x() * qreal(-0.167),
1455 prev.y() * qreal(0.167) +
1457 next.y() * qreal(-0.167));
1459 path.cubicTo(control1, control2, point);
1462 /****************************************************************************/
1465 \qmlclass PathArc QDeclarativePathArc
1466 \inqmlmodule QtQuick 2
1467 \ingroup qml-view-elements
1468 \brief The PathArc defines an arc with the given radius.
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.
1473 The following QML produces the path shown below:
1476 \o \image declarative-patharc.png
1477 \o \snippet doc/src/snippets/declarative/path/basicarc.qml 0
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.
1483 \sa Path, PathLine, PathQuad, PathCubic, PathCurve, PathSvg
1487 \qmlproperty real QtQuick2::PathArc::x
1488 \qmlproperty real QtQuick2::PathArc::y
1490 Defines the end point of the arc.
1492 \sa relativeX, relativeY
1496 \qmlproperty real QtQuick2::PathArc::relativeX
1497 \qmlproperty real QtQuick2::PathArc::relativeY
1499 Defines the end point of the arc relative to its start.
1501 If both a relative and absolute end position are specified for a single axis, the relative
1502 position will be used.
1504 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1511 \qmlproperty real QtQuick2::PathArc::radiusX
1512 \qmlproperty real QtQuick2::PathArc::radiusY
1514 Defines the radius of the arc.
1516 The following QML demonstrates how different radius values can be used to change
1517 the shape of the arc:
1520 \o \image declarative-arcradius.png
1521 \o \snippet doc/src/snippets/declarative/path/arcradius.qml 0
1525 qreal QDeclarativePathArc::radiusX() const
1530 void QDeclarativePathArc::setRadiusX(qreal radius)
1532 if (_radiusX == radius)
1536 emit radiusXChanged();
1539 qreal QDeclarativePathArc::radiusY() const
1544 void QDeclarativePathArc::setRadiusY(qreal radius)
1546 if (_radiusY == radius)
1550 emit radiusYChanged();
1554 \qmlproperty bool QtQuick2::PathArc::useLargeArc
1555 Whether to use a large arc as defined by the arc points.
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.
1565 \o \image declarative-largearc.png
1566 \o \snippet doc/src/snippets/declarative/path/largearc.qml 0
1569 The default value is false.
1572 bool QDeclarativePathArc::useLargeArc() const
1574 return _useLargeArc;
1577 void QDeclarativePathArc::setUseLargeArc(bool largeArc)
1579 if (_useLargeArc == largeArc)
1582 _useLargeArc = largeArc;
1583 emit useLargeArcChanged();
1587 \qmlproperty enum QtQuick2::PathArc::direction
1589 Defines the direction of the arc. Possible values are
1590 PathArc.Clockwise (default) and PathArc.Counterclockwise.
1592 The following QML can produce either of the two illustrated arcs below
1593 by changing the value of direction.
1596 \o \image declarative-arcdirection.png
1597 \o \snippet doc/src/snippets/declarative/path/arcdirection.qml 0
1603 QDeclarativePathArc::ArcDirection QDeclarativePathArc::direction() const
1608 void QDeclarativePathArc::setDirection(ArcDirection direction)
1610 if (_direction == direction)
1613 _direction = direction;
1614 emit directionChanged();
1617 void QDeclarativePathArc::addToPath(QPainterPath &path, const QDeclarativePathData &data)
1619 const QPointF &startPoint = path.currentPosition();
1620 const QPointF &endPoint = positionForCurve(data, startPoint);
1621 QDeclarativeSvgParser::pathArc(path,
1626 _direction == Clockwise ? 1 : 0,
1629 startPoint.x(), startPoint.y());
1632 /****************************************************************************/
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.
1640 The following QML produces the path shown below:
1643 \o \image declarative-pathsvg.png
1647 startX: 50; startY: 50
1648 PathSvg { path: "L 150 50 L 100 150 z" }
1653 \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathCurve
1657 \qmlproperty string QtQuick2::PathSvg::path
1659 The SVG path data string specifying the path.
1661 See \l {http://www.w3.org/TR/SVG/paths.html#PathData}{W3C SVG Path Data}
1662 for more details on this format.
1665 QString QDeclarativePathSvg::path() const
1670 void QDeclarativePathSvg::setPath(const QString &path)
1679 void QDeclarativePathSvg::addToPath(QPainterPath &path, const QDeclarativePathData &)
1681 QDeclarativeSvgParser::parsePathDataFast(_path, path);
1684 /****************************************************************************/
1687 \qmlclass PathPercent QDeclarativePathPercent
1688 \inqmlmodule QtQuick 2
1689 \ingroup qml-view-elements
1690 \brief The PathPercent manipulates the way a path is interpreted.
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.
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.
1701 \o \image declarative-nopercent.png
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 }
1715 \o \image declarative-percent.png
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 }
1737 \qmlproperty real QtQuick2::PathPercent::value
1738 The proportion of items that should be laid out up to this point.
1740 This value should always be higher than the last value specified
1741 by a PathPercent at a previous position in the Path.
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
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 }
1766 qreal QDeclarativePathPercent::value() const
1771 void QDeclarativePathPercent::setValue(qreal value)
1773 if (_value != value) {
1775 emit valueChanged();