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 qtquick-animation-paths
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 qtquick-animation-paths
71 \brief 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 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 static inline int segmentCount(const QPainterPath &path, qreal pathLength)
460 // In the really simple case of a single straight line we can interpolate without jitter
461 // between just two points.
462 if (path.elementCount() == 2
463 && path.elementAt(0).type == QPainterPath::MoveToElement
464 && path.elementAt(1).type == QPainterPath::LineToElement) {
467 // more points means less jitter between items as they move along the
468 // path, but takes longer to generate
469 return qCeil(pathLength*5);
472 //derivative of the equation
473 static inline qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d)
475 return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
478 void QQuickPath::createPointCache() const
480 Q_D(const QQuickPath);
481 qreal pathLength = d->pathLength;
482 if (pathLength <= 0 || qIsNaN(pathLength))
485 const int segments = segmentCount(d->_path, pathLength);
486 const int lastElement = d->_path.elementCount() - 1;
487 d->_pointCache.resize(segments+1);
489 int currElement = -1;
491 QBezier currBez = nextBezier(d->_path, &currElement, &bezLength);
492 qreal currLength = bezLength;
493 qreal epc = currLength / pathLength;
495 for (int i = 0; i < d->_pointCache.size(); i++) {
496 //find which set we are in
497 qreal prevPercent = 0;
498 qreal prevOrigPercent = 0;
499 for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
500 qreal percent = qreal(i)/segments;
501 const AttributePoint &point = d->_attributePoints.at(ii);
502 if (percent < point.percent || ii == d->_attributePoints.count() - 1) { //### || is special case for very last item
503 qreal elementPercent = (percent - prevPercent);
505 qreal spc = prevOrigPercent + elementPercent * point.scale;
508 if (currElement > lastElement)
510 currBez = nextBezier(d->_path, &currElement, &bezLength);
511 if (bezLength == 0.0) {
512 currLength = pathLength;
516 currLength += bezLength;
517 epc = currLength / pathLength;
519 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
520 d->_pointCache[i] = currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
523 prevOrigPercent = point.origpercent;
524 prevPercent = point.percent;
529 void QQuickPath::invalidateSequentialHistory() const
531 Q_D(const QQuickPath);
532 d->prevBez.isValid = false;
535 QPointF QQuickPath::sequentialPointAt(qreal p, qreal *angle) const
537 Q_D(const QQuickPath);
538 return sequentialPointAt(d->_path, d->pathLength, d->_attributePoints, d->prevBez, p, angle);
541 QPointF QQuickPath::sequentialPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
543 Q_ASSERT(p >= 0.0 && p <= 1.0);
545 if (!prevBez.isValid)
546 return p > .5 ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
547 forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
549 return p < prevBez.p ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
550 forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
553 QPointF QQuickPath::forwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
555 if (pathLength <= 0 || qIsNaN(pathLength))
556 return path.pointAtPercent(0); //expensive?
558 const int lastElement = path.elementCount() - 1;
559 bool haveCachedBez = prevBez.isValid;
560 int currElement = haveCachedBez ? prevBez.element : -1;
561 qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
562 QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength);
563 qreal currLength = haveCachedBez ? prevBez.currLength : bezLength;
564 qreal epc = currLength / pathLength;
566 //find which set we are in
567 qreal prevPercent = 0;
568 qreal prevOrigPercent = 0;
569 for (int ii = 0; ii < attributePoints.count(); ++ii) {
571 const AttributePoint &point = attributePoints.at(ii);
572 if (percent < point.percent || ii == attributePoints.count() - 1) {
573 qreal elementPercent = (percent - prevPercent);
575 qreal spc = prevOrigPercent + elementPercent * point.scale;
578 Q_ASSERT(!(currElement > lastElement));
579 Q_UNUSED(lastElement);
580 currBez = nextBezier(path, &currElement, &bezLength);
581 currLength += bezLength;
582 epc = currLength / pathLength;
584 prevBez.element = currElement;
585 prevBez.bezLength = bezLength;
586 prevBez.currLength = currLength;
587 prevBez.bezier = currBez;
589 prevBez.isValid = true;
591 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
594 qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
595 qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
596 *angle = QLineF(0, 0, m1, m2).angle();
599 return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
601 prevOrigPercent = point.origpercent;
602 prevPercent = point.percent;
608 //ideally this should be merged with forwardsPointAt
609 QPointF QQuickPath::backwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
611 if (pathLength <= 0 || qIsNaN(pathLength))
612 return path.pointAtPercent(0);
614 const int firstElement = 1; //element 0 is always a MoveTo, which we ignore
615 bool haveCachedBez = prevBez.isValid;
616 int currElement = haveCachedBez ? prevBez.element : path.elementCount();
617 qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
618 QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength, true /*reverse*/);
619 qreal currLength = haveCachedBez ? prevBez.currLength : pathLength;
620 qreal prevLength = currLength - bezLength;
621 qreal epc = prevLength / pathLength;
623 for (int ii = attributePoints.count() - 1; ii > 0; --ii) {
625 const AttributePoint &point = attributePoints.at(ii);
626 const AttributePoint &prevPoint = attributePoints.at(ii-1);
627 if (percent > prevPoint.percent || ii == 1) {
628 qreal elementPercent = (percent - prevPoint.percent);
630 qreal spc = prevPoint.origpercent + elementPercent * point.scale;
633 Q_ASSERT(!(currElement < firstElement));
634 Q_UNUSED(firstElement);
635 currBez = nextBezier(path, &currElement, &bezLength, true /*reverse*/);
636 //special case for first element is to avoid floating point math
637 //causing an epc that never hits 0.
638 currLength = (currElement == firstElement) ? bezLength : prevLength;
639 prevLength = currLength - bezLength;
640 epc = prevLength / pathLength;
642 prevBez.element = currElement;
643 prevBez.bezLength = bezLength;
644 prevBez.currLength = currLength;
645 prevBez.bezier = currBez;
647 prevBez.isValid = true;
649 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
652 qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
653 qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
654 *angle = QLineF(0, 0, m1, m2).angle();
657 return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
664 QPointF QQuickPath::pointAt(qreal p) const
666 Q_D(const QQuickPath);
667 if (d->_pointCache.isEmpty()) {
669 if (d->_pointCache.isEmpty())
673 const int segmentCount = d->_pointCache.size() - 1;
674 qreal idxf = p*segmentCount;
675 int idx1 = qFloor(idxf);
676 qreal delta = idxf - idx1;
677 if (idx1 > segmentCount)
683 return d->_pointCache.at(idx1);
685 // interpolate between the two points.
686 int idx2 = qCeil(idxf);
687 if (idx2 > segmentCount)
692 QPointF p1 = d->_pointCache.at(idx1);
693 QPointF p2 = d->_pointCache.at(idx2);
694 QPointF pos = p1 * (1.0-delta) + p2 * delta;
699 qreal QQuickPath::attributeAt(const QString &name, qreal percent) const
701 Q_D(const QQuickPath);
702 if (percent < 0 || percent > 1)
705 for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
706 const AttributePoint &point = d->_attributePoints.at(ii);
708 if (point.percent == percent) {
709 return point.values.value(name);
710 } else if (point.percent > percent) {
712 ii?(d->_attributePoints.at(ii - 1).values.value(name)):0;
714 ii?(d->_attributePoints.at(ii - 1).percent):0;
715 qreal curValue = point.values.value(name);
716 qreal curPercent = point.percent;
718 return lastValue + (curValue - lastValue) * (percent - lastPercent) / (curPercent - lastPercent);
725 /****************************************************************************/
727 qreal QQuickCurve::x() const
729 return _x.isNull ? 0 : _x.value;
732 void QQuickCurve::setX(qreal x)
734 if (_x.isNull || _x != x) {
741 bool QQuickCurve::hasX()
746 qreal QQuickCurve::y() const
748 return _y.isNull ? 0 : _y.value;
751 void QQuickCurve::setY(qreal y)
753 if (_y.isNull || _y != y) {
760 bool QQuickCurve::hasY()
765 qreal QQuickCurve::relativeX() const
770 void QQuickCurve::setRelativeX(qreal x)
772 if (_relativeX.isNull || _relativeX != x) {
774 emit relativeXChanged();
779 bool QQuickCurve::hasRelativeX()
781 return _relativeX.isValid();
784 qreal QQuickCurve::relativeY() const
789 void QQuickCurve::setRelativeY(qreal y)
791 if (_relativeY.isNull || _relativeY != y) {
793 emit relativeYChanged();
798 bool QQuickCurve::hasRelativeY()
800 return _relativeY.isValid();
803 /****************************************************************************/
806 \qmlclass PathAttribute QQuickPathAttribute
807 \inqmlmodule QtQuick 2
808 \ingroup qtquick-animation-paths
809 \brief Specifies how to set an attribute at a given position in a Path
811 The PathAttribute object allows attributes consisting of a name and
812 a value to be specified for various points along a path. The
813 attributes are exposed to the delegate as
814 \l{qdeclarativeintroduction.html#attached-properties} {Attached Properties}.
815 The value of an attribute at any particular point along the path is interpolated
816 from the PathAttributes bounding that point.
818 The example below shows a path with the items scaled to 30% with
819 opacity 50% at the top of the path and scaled 100% with opacity
820 100% at the bottom. Note the use of the PathView.iconScale and
821 PathView.iconOpacity attached properties to set the scale and opacity
826 \li \image declarative-pathattribute.png
828 \snippet qml/pathview/pathattributes.qml 0
829 (see the PathView documentation for the specification of ContactModel.qml
830 used for ContactModel above.)
838 \qmlproperty string QtQuick2::PathAttribute::name
839 This property holds the name of the attribute to change.
841 This attribute will be available to the delegate as PathView.<name>
843 Note that using an existing Item property name such as "opacity" as an
844 attribute is allowed. This is because path attributes add a new
845 \l{qdeclarativeintroduction.html#attached-properties} {Attached Property}
846 which in no way clashes with existing properties.
850 the name of the attribute to change.
853 QString QQuickPathAttribute::name() const
858 void QQuickPathAttribute::setName(const QString &name)
867 \qmlproperty real QtQuick2::PathAttribute::value
868 This property holds the value for the attribute.
870 The value specified can be used to influence the visual appearance
871 of an item along the path. For example, the following Path specifies
872 an attribute named \e itemRotation, which has the value \e 0 at the
873 beginning of the path, and the value 90 at the end of the path.
879 PathAttribute { name: "itemRotation"; value: 0 }
880 PathLine { x: 100; y: 100 }
881 PathAttribute { name: "itemRotation"; value: 90 }
885 In our delegate, we can then bind the \e rotation property to the
886 \l{qdeclarativeintroduction.html#attached-properties} {Attached Property}
887 \e PathView.itemRotation created for this attribute.
891 width: 10; height: 10
892 rotation: PathView.itemRotation
896 As each item is positioned along the path, it will be rotated accordingly:
897 an item at the beginning of the path with be not be rotated, an item at
898 the end of the path will be rotated 90 degrees, and an item mid-way along
899 the path will be rotated 45 degrees.
903 the new value of the attribute.
905 qreal QQuickPathAttribute::value() const
910 void QQuickPathAttribute::setValue(qreal value)
912 if (_value != value) {
919 /****************************************************************************/
922 \qmlclass PathLine QQuickPathLine
923 \inqmlmodule QtQuick 2
924 \ingroup qtquick-animation-paths
925 \brief Defines a straight line
927 The example below creates a path consisting of a straight line from
932 startX: 0; startY: 100
933 PathLine { x: 200; y: 100 }
937 \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
941 \qmlproperty real QtQuick2::PathLine::x
942 \qmlproperty real QtQuick2::PathLine::y
944 Defines the end point of the line.
946 \sa relativeX, relativeY
950 \qmlproperty real QtQuick2::PathLine::relativeX
951 \qmlproperty real QtQuick2::PathLine::relativeY
953 Defines the end point of the line relative to its start.
955 If both a relative and absolute end position are specified for a single axis, the relative
956 position will be used.
958 Relative and absolute positions can be mixed, for example it is valid to set a relative x
964 inline QPointF positionForCurve(const QQuickPathData &data, const QPointF &prevPoint)
966 QQuickCurve *curve = data.curves.at(data.index);
967 bool isEnd = data.index == data.curves.size() - 1;
968 return QPointF(curve->hasRelativeX() ? prevPoint.x() + curve->relativeX() : !isEnd || curve->hasX() ? curve->x() : data.endPoint.x(),
969 curve->hasRelativeY() ? prevPoint.y() + curve->relativeY() : !isEnd || curve->hasY() ? curve->y() : data.endPoint.y());
972 void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data)
974 path.lineTo(positionForCurve(data, path.currentPosition()));
977 /****************************************************************************/
980 \qmlclass PathQuad QQuickPathQuad
981 \inqmlmodule QtQuick 2
982 \ingroup qtquick-animation-paths
983 \brief Defines a quadratic Bezier curve with a control point
985 The following QML produces the path shown below:
988 \li \image declarative-pathquad.png
993 PathQuad { x: 200; y: 0; controlX: 100; controlY: 150 }
998 \sa Path, PathCubic, PathLine, PathArc, PathCurve, PathSvg
1002 \qmlproperty real QtQuick2::PathQuad::x
1003 \qmlproperty real QtQuick2::PathQuad::y
1005 Defines the end point of the curve.
1007 \sa relativeX, relativeY
1011 \qmlproperty real QtQuick2::PathQuad::relativeX
1012 \qmlproperty real QtQuick2::PathQuad::relativeY
1014 Defines the end point of the curve relative to its start.
1016 If both a relative and absolute end position are specified for a single axis, the relative
1017 position will be used.
1019 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1026 \qmlproperty real QtQuick2::PathQuad::controlX
1027 \qmlproperty real QtQuick2::PathQuad::controlY
1029 Defines the position of the control point.
1033 the x position of the control point.
1035 qreal QQuickPathQuad::controlX() const
1040 void QQuickPathQuad::setControlX(qreal x)
1042 if (_controlX != x) {
1044 emit controlXChanged();
1051 the y position of the control point.
1053 qreal QQuickPathQuad::controlY() const
1058 void QQuickPathQuad::setControlY(qreal y)
1060 if (_controlY != y) {
1062 emit controlYChanged();
1068 \qmlproperty real QtQuick2::PathCubic::relativeControlX
1069 \qmlproperty real QtQuick2::PathCubic::relativeControlY
1071 Defines the position of the control point relative to the curve's start.
1073 If both a relative and absolute control position are specified for a single axis, the relative
1074 position will be used.
1076 Relative and absolute positions can be mixed, for example it is valid to set a relative control x
1077 and an absolute control y.
1079 \sa controlX, controlY
1082 qreal QQuickPathQuad::relativeControlX() const
1084 return _relativeControlX;
1087 void QQuickPathQuad::setRelativeControlX(qreal x)
1089 if (_relativeControlX.isNull || _relativeControlX != x) {
1090 _relativeControlX = x;
1091 emit relativeControlXChanged();
1096 bool QQuickPathQuad::hasRelativeControlX()
1098 return _relativeControlX.isValid();
1101 qreal QQuickPathQuad::relativeControlY() const
1103 return _relativeControlY;
1106 void QQuickPathQuad::setRelativeControlY(qreal y)
1108 if (_relativeControlY.isNull || _relativeControlY != y) {
1109 _relativeControlY = y;
1110 emit relativeControlYChanged();
1115 bool QQuickPathQuad::hasRelativeControlY()
1117 return _relativeControlY.isValid();
1120 void QQuickPathQuad::addToPath(QPainterPath &path, const QQuickPathData &data)
1122 const QPointF &prevPoint = path.currentPosition();
1123 QPointF controlPoint(hasRelativeControlX() ? prevPoint.x() + relativeControlX() : controlX(),
1124 hasRelativeControlY() ? prevPoint.y() + relativeControlY() : controlY());
1125 path.quadTo(controlPoint, positionForCurve(data, path.currentPosition()));
1128 /****************************************************************************/
1131 \qmlclass PathCubic QQuickPathCubic
1132 \inqmlmodule QtQuick 2
1133 \ingroup qtquick-animation-paths
1134 \brief Defines a cubic Bezier curve with two control points
1136 The following QML produces the path shown below:
1139 \li \image declarative-pathcubic.png
1143 startX: 20; startY: 0
1146 control1X: -10; control1Y: 90
1147 control2X: 210; control2Y: 90
1153 \sa Path, PathQuad, PathLine, PathArc, PathCurve, PathSvg
1157 \qmlproperty real QtQuick2::PathCubic::x
1158 \qmlproperty real QtQuick2::PathCubic::y
1160 Defines the end point of the curve.
1162 \sa relativeX, relativeY
1166 \qmlproperty real QtQuick2::PathCubic::relativeX
1167 \qmlproperty real QtQuick2::PathCubic::relativeY
1169 Defines the end point of the curve relative to its start.
1171 If both a relative and absolute end position are specified for a single axis, the relative
1172 position will be used.
1174 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1181 \qmlproperty real QtQuick2::PathCubic::control1X
1182 \qmlproperty real QtQuick2::PathCubic::control1Y
1184 Defines the position of the first control point.
1186 qreal QQuickPathCubic::control1X() const
1191 void QQuickPathCubic::setControl1X(qreal x)
1193 if (_control1X != x) {
1195 emit control1XChanged();
1200 qreal QQuickPathCubic::control1Y() const
1205 void QQuickPathCubic::setControl1Y(qreal y)
1207 if (_control1Y != y) {
1209 emit control1YChanged();
1215 \qmlproperty real QtQuick2::PathCubic::control2X
1216 \qmlproperty real QtQuick2::PathCubic::control2Y
1218 Defines the position of the second control point.
1220 qreal QQuickPathCubic::control2X() const
1225 void QQuickPathCubic::setControl2X(qreal x)
1227 if (_control2X != x) {
1229 emit control2XChanged();
1234 qreal QQuickPathCubic::control2Y() const
1239 void QQuickPathCubic::setControl2Y(qreal y)
1241 if (_control2Y != y) {
1243 emit control2YChanged();
1249 \qmlproperty real QtQuick2::PathCubic::relativeControl1X
1250 \qmlproperty real QtQuick2::PathCubic::relativeControl1Y
1251 \qmlproperty real QtQuick2::PathCubic::relativeControl2X
1252 \qmlproperty real QtQuick2::PathCubic::relativeControl2Y
1254 Defines the positions of the control points relative to the curve's start.
1256 If both a relative and absolute control position are specified for a control point's axis, the relative
1257 position will be used.
1259 Relative and absolute positions can be mixed, for example it is valid to set a relative control1 x
1260 and an absolute control1 y.
1262 \sa control1X, control1Y, control2X, control2Y
1265 qreal QQuickPathCubic::relativeControl1X() const
1267 return _relativeControl1X;
1270 void QQuickPathCubic::setRelativeControl1X(qreal x)
1272 if (_relativeControl1X.isNull || _relativeControl1X != x) {
1273 _relativeControl1X = x;
1274 emit relativeControl1XChanged();
1279 bool QQuickPathCubic::hasRelativeControl1X()
1281 return _relativeControl1X.isValid();
1284 qreal QQuickPathCubic::relativeControl1Y() const
1286 return _relativeControl1Y;
1289 void QQuickPathCubic::setRelativeControl1Y(qreal y)
1291 if (_relativeControl1Y.isNull || _relativeControl1Y != y) {
1292 _relativeControl1Y = y;
1293 emit relativeControl1YChanged();
1298 bool QQuickPathCubic::hasRelativeControl1Y()
1300 return _relativeControl1Y.isValid();
1303 qreal QQuickPathCubic::relativeControl2X() const
1305 return _relativeControl2X;
1308 void QQuickPathCubic::setRelativeControl2X(qreal x)
1310 if (_relativeControl2X.isNull || _relativeControl2X != x) {
1311 _relativeControl2X = x;
1312 emit relativeControl2XChanged();
1317 bool QQuickPathCubic::hasRelativeControl2X()
1319 return _relativeControl2X.isValid();
1322 qreal QQuickPathCubic::relativeControl2Y() const
1324 return _relativeControl2Y;
1327 void QQuickPathCubic::setRelativeControl2Y(qreal y)
1329 if (_relativeControl2Y.isNull || _relativeControl2Y != y) {
1330 _relativeControl2Y = y;
1331 emit relativeControl2YChanged();
1336 bool QQuickPathCubic::hasRelativeControl2Y()
1338 return _relativeControl2Y.isValid();
1341 void QQuickPathCubic::addToPath(QPainterPath &path, const QQuickPathData &data)
1343 const QPointF &prevPoint = path.currentPosition();
1344 QPointF controlPoint1(hasRelativeControl1X() ? prevPoint.x() + relativeControl1X() : control1X(),
1345 hasRelativeControl1Y() ? prevPoint.y() + relativeControl1Y() : control1Y());
1346 QPointF controlPoint2(hasRelativeControl2X() ? prevPoint.x() + relativeControl2X() : control2X(),
1347 hasRelativeControl2Y() ? prevPoint.y() + relativeControl2Y() : control2Y());
1348 path.cubicTo(controlPoint1, controlPoint2, positionForCurve(data, path.currentPosition()));
1351 /****************************************************************************/
1354 \qmlclass PathCurve QQuickPathCurve
1355 \inqmlmodule QtQuick 2
1356 \ingroup qtquick-animation-paths
1357 \brief Defines a point on a Catmull-Rom curve
1359 PathCurve provides an easy way to specify a curve passing directly through a set of points.
1360 Typically multiple PathCurves are used in a series, as the following example demonstrates:
1362 \snippet qml/path/basiccurve.qml 0
1364 This example produces the following path (with the starting point and PathCurve points
1365 highlighted in red):
1367 \image declarative-pathcurve.png
1369 \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathSvg
1373 \qmlproperty real QtQuick2::PathCurve::x
1374 \qmlproperty real QtQuick2::PathCurve::y
1376 Defines the end point of the curve.
1378 \sa relativeX, relativeY
1382 \qmlproperty real QtQuick2::PathCurve::relativeX
1383 \qmlproperty real QtQuick2::PathCurve::relativeY
1385 Defines the end point of the curve relative to its start.
1387 If both a relative and absolute end position are specified for a single axis, the relative
1388 position will be used.
1390 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1396 inline QPointF previousPathPosition(const QPainterPath &path)
1398 int count = path.elementCount();
1402 int index = path.elementAt(count-1).type == QPainterPath::CurveToDataElement ? count - 4 : count - 2;
1403 return index > -1 ? QPointF(path.elementAt(index)) : path.pointAtPercent(0);
1406 void QQuickPathCatmullRomCurve::addToPath(QPainterPath &path, const QQuickPathData &data)
1408 //here we convert catmull-rom spline to bezier for use in QPainterPath.
1409 //basic conversion algorithm:
1410 // catmull-rom points * inverse bezier matrix * catmull-rom matrix = bezier points
1411 //each point in the catmull-rom spline produces a bezier endpoint + 2 control points
1412 //calculations for each point use a moving window of 4 points
1413 // (previous 2 points + current point + next point)
1414 QPointF prevFar, prev, point, next;
1416 //get previous points
1417 int index = data.index - 1;
1418 QQuickCurve *curve = index == -1 ? 0 : data.curves.at(index);
1419 if (qobject_cast<QQuickPathCatmullRomCurve*>(curve)) {
1420 prev = path.currentPosition();
1421 prevFar = previousPathPosition(path);
1423 prev = path.currentPosition();
1424 bool prevFarSet = false;
1425 if (index == -1 && data.curves.count() > 1) {
1426 if (qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(data.curves.count()-1))) {
1427 //TODO: profile and optimize
1429 QQuickPathData loopData;
1430 loopData.endPoint = data.endPoint;
1431 loopData.curves = data.curves;
1432 for (int i = data.index; i < data.curves.count(); ++i) {
1434 pos = positionForCurve(loopData, pos);
1435 if (i == data.curves.count()-2)
1438 if (pos == QPointF(path.elementAt(0))) {
1439 //this is a closed path starting and ending with catmull-rom segments.
1440 //we try to smooth the join point
1450 point = positionForCurve(data, path.currentPosition());
1453 index = data.index + 1;
1454 if (index < data.curves.count() && qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(index))) {
1455 QQuickPathData nextData;
1456 nextData.index = index;
1457 nextData.endPoint = data.endPoint;
1458 nextData.curves = data.curves;
1459 next = positionForCurve(nextData, point);
1461 if (point == QPointF(path.elementAt(0)) && qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(0))) {
1462 //this is a closed path starting and ending with catmull-rom segments.
1463 //we try to smooth the join point
1464 next = QPointF(path.elementAt(3)); //the first catmull-rom point
1470 full conversion matrix (inverse bezier * catmull-rom):
1471 0.000, 1.000, 0.000, 0.000,
1472 -0.167, 1.000, 0.167, 0.000,
1473 0.000, 0.167, 1.000, -0.167,
1474 0.000, 0.000, 1.000, 0.000
1476 conversion doesn't require full matrix multiplication,
1477 so below we simplify
1479 QPointF control1(prevFar.x() * qreal(-0.167) +
1481 point.x() * qreal(0.167),
1482 prevFar.y() * qreal(-0.167) +
1484 point.y() * qreal(0.167));
1486 QPointF control2(prev.x() * qreal(0.167) +
1488 next.x() * qreal(-0.167),
1489 prev.y() * qreal(0.167) +
1491 next.y() * qreal(-0.167));
1493 path.cubicTo(control1, control2, point);
1496 /****************************************************************************/
1499 \qmlclass PathArc QQuickPathArc
1500 \inqmlmodule QtQuick 2
1501 \ingroup qtquick-animation-paths
1502 \brief Defines an arc with the given radius
1504 PathArc provides a simple way of specifying an arc that ends at a given position
1505 and uses the specified radius. It is modeled after the SVG elliptical arc command.
1507 The following QML produces the path shown below:
1510 \li \image declarative-patharc.png
1511 \li \snippet qml/path/basicarc.qml 0
1514 Note that a single PathArc cannot be used to specify a circle. Instead, you can
1515 use two PathArc elements, each specifying half of the circle.
1517 \sa Path, PathLine, PathQuad, PathCubic, PathCurve, PathSvg
1521 \qmlproperty real QtQuick2::PathArc::x
1522 \qmlproperty real QtQuick2::PathArc::y
1524 Defines the end point of the arc.
1526 \sa relativeX, relativeY
1530 \qmlproperty real QtQuick2::PathArc::relativeX
1531 \qmlproperty real QtQuick2::PathArc::relativeY
1533 Defines the end point of the arc relative to its start.
1535 If both a relative and absolute end position are specified for a single axis, the relative
1536 position will be used.
1538 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1545 \qmlproperty real QtQuick2::PathArc::radiusX
1546 \qmlproperty real QtQuick2::PathArc::radiusY
1548 Defines the radius of the arc.
1550 The following QML demonstrates how different radius values can be used to change
1551 the shape of the arc:
1554 \li \image declarative-arcradius.png
1555 \li \snippet qml/path/arcradius.qml 0
1559 qreal QQuickPathArc::radiusX() const
1564 void QQuickPathArc::setRadiusX(qreal radius)
1566 if (_radiusX == radius)
1570 emit radiusXChanged();
1573 qreal QQuickPathArc::radiusY() const
1578 void QQuickPathArc::setRadiusY(qreal radius)
1580 if (_radiusY == radius)
1584 emit radiusYChanged();
1588 \qmlproperty bool QtQuick2::PathArc::useLargeArc
1589 Whether to use a large arc as defined by the arc points.
1591 Given fixed start and end positions, radius, and direction,
1592 there are two possible arcs that can fit the data. useLargeArc
1593 is used to distinguish between these. For example, the following
1594 QML can produce either of the two illustrated arcs below by
1595 changing the value of useLargeArc.
1599 \li \image declarative-largearc.png
1600 \li \snippet qml/path/largearc.qml 0
1603 The default value is false.
1606 bool QQuickPathArc::useLargeArc() const
1608 return _useLargeArc;
1611 void QQuickPathArc::setUseLargeArc(bool largeArc)
1613 if (_useLargeArc == largeArc)
1616 _useLargeArc = largeArc;
1617 emit useLargeArcChanged();
1621 \qmlproperty enum QtQuick2::PathArc::direction
1623 Defines the direction of the arc. Possible values are
1624 PathArc.Clockwise (default) and PathArc.Counterclockwise.
1626 The following QML can produce either of the two illustrated arcs below
1627 by changing the value of direction.
1630 \li \image declarative-arcdirection.png
1631 \li \snippet qml/path/arcdirection.qml 0
1637 QQuickPathArc::ArcDirection QQuickPathArc::direction() const
1642 void QQuickPathArc::setDirection(ArcDirection direction)
1644 if (_direction == direction)
1647 _direction = direction;
1648 emit directionChanged();
1651 void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data)
1653 const QPointF &startPoint = path.currentPosition();
1654 const QPointF &endPoint = positionForCurve(data, startPoint);
1655 QQuickSvgParser::pathArc(path,
1660 _direction == Clockwise ? 1 : 0,
1663 startPoint.x(), startPoint.y());
1666 /****************************************************************************/
1669 \qmlclass PathSvg QQuickPathSvg
1670 \inqmlmodule QtQuick 2
1671 \ingroup qtquick-animation-paths
1672 \brief Defines a path using an SVG path data string
1674 The following QML produces the path shown below:
1677 \li \image declarative-pathsvg.png
1681 startX: 50; startY: 50
1682 PathSvg { path: "L 150 50 L 100 150 z" }
1687 \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathCurve
1691 \qmlproperty string QtQuick2::PathSvg::path
1693 The SVG path data string specifying the path.
1695 See \l {http://www.w3.org/TR/SVG/paths.html#PathData}{W3C SVG Path Data}
1696 for more details on this format.
1699 QString QQuickPathSvg::path() const
1704 void QQuickPathSvg::setPath(const QString &path)
1713 void QQuickPathSvg::addToPath(QPainterPath &path, const QQuickPathData &)
1715 QQuickSvgParser::parsePathDataFast(_path, path);
1718 /****************************************************************************/
1721 \qmlclass PathPercent QQuickPathPercent
1722 \inqmlmodule QtQuick 2
1723 \ingroup qtquick-animation-paths
1724 \brief Manipulates the way a path is interpreted
1726 PathPercent allows you to manipulate the spacing between items on a
1727 PathView's path. You can use it to bunch together items on part of
1728 the path, and spread them out on other parts of the path.
1730 The examples below show the normal distribution of items along a path
1731 compared to a distribution which places 50% of the items along the
1732 PathLine section of the path.
1735 \li \image declarative-nopercent.png
1741 startX: 20; startY: 0
1742 PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
1743 PathLine { x: 150; y: 80 }
1744 PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
1749 \li \image declarative-percent.png
1755 startX: 20; startY: 0
1756 PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
1757 PathPercent { value: 0.25 }
1758 PathLine { x: 150; y: 80 }
1759 PathPercent { value: 0.75 }
1760 PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
1761 PathPercent { value: 1 }
1771 \qmlproperty real QtQuick2::PathPercent::value
1772 The proportion of items that should be laid out up to this point.
1774 This value should always be higher than the last value specified
1775 by a PathPercent at a previous position in the Path.
1777 In the following example we have a Path made up of three PathLines.
1778 Normally, the items of the PathView would be laid out equally along
1779 this path, with an equal number of items per line segment. PathPercent
1780 allows us to specify that the first and third lines should each hold
1781 10% of the laid out items, while the second line should hold the remaining
1788 startX: 0; startY: 0
1789 PathLine { x:100; y: 0; }
1790 PathPercent { value: 0.1 }
1791 PathLine { x: 100; y: 100 }
1792 PathPercent { value: 0.9 }
1793 PathLine { x: 100; y: 0 }
1794 PathPercent { value: 1 }
1800 qreal QQuickPathPercent::value() const
1805 void QQuickPathPercent::setValue(qreal value)
1807 if (_value != value) {
1809 emit valueChanged();