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>
57 \instantiates QQuickPathElement
58 \inqmlmodule QtQuick 2
59 \ingroup qtquick-animation-paths
60 \brief PathElement is the base path type
62 This type is the base for all path types. It cannot
65 \sa Path, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
70 \instantiates QQuickPath
71 \inqmlmodule QtQuick 2
72 \ingroup qtquick-animation-paths
73 \brief Defines a path for use by \l PathView
75 A Path is composed of one or more path segments - PathLine, PathQuad,
76 PathCubic, PathArc, PathCurve, PathSvg.
78 The spacing of the items along the Path can be adjusted via a
81 PathAttribute allows named attributes with values to be defined
84 \sa PathView, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
86 QQuickPath::QQuickPath(QObject *parent)
87 : QObject(*(new QQuickPathPrivate), parent)
91 QQuickPath::~QQuickPath()
96 \qmlproperty real QtQuick2::Path::startX
97 \qmlproperty real QtQuick2::Path::startY
98 These properties hold the starting position of the path.
100 qreal QQuickPath::startX() const
102 Q_D(const QQuickPath);
103 return d->startX.isNull ? 0 : d->startX.value;
106 void QQuickPath::setStartX(qreal x)
109 if (d->startX.isValid() && qFuzzyCompare(x, d->startX))
112 emit startXChanged();
116 bool QQuickPath::hasStartX() const
118 Q_D(const QQuickPath);
119 return d->startX.isValid();
122 qreal QQuickPath::startY() const
124 Q_D(const QQuickPath);
125 return d->startY.isNull ? 0 : d->startY.value;
128 void QQuickPath::setStartY(qreal y)
131 if (d->startY.isValid() && qFuzzyCompare(y, d->startY))
134 emit startYChanged();
138 bool QQuickPath::hasStartY() const
140 Q_D(const QQuickPath);
141 return d->startY.isValid();
145 \qmlproperty bool QtQuick2::Path::closed
146 This property holds whether the start and end of the path are identical.
148 bool QQuickPath::isClosed() const
150 Q_D(const QQuickPath);
154 bool QQuickPath::hasEnd() const
156 Q_D(const QQuickPath);
157 for (int i = d->_pathElements.count() - 1; i > -1; --i) {
158 if (QQuickCurve *curve = qobject_cast<QQuickCurve *>(d->_pathElements.at(i))) {
159 if ((!curve->hasX() && !curve->hasRelativeX()) || (!curve->hasY() && !curve->hasRelativeY()))
165 return hasStartX() && hasStartY();
169 \qmlproperty list<PathElement> QtQuick2::Path::pathElements
170 This property holds the objects composing the path.
174 A path can contain the following path objects:
176 \li \l PathLine - a straight line to a given position.
177 \li \l PathQuad - a quadratic Bezier curve to a given position with a control point.
178 \li \l PathCubic - a cubic Bezier curve to a given position with two control points.
179 \li \l PathArc - an arc to a given position with a radius.
180 \li \l PathSvg - a path specified as an SVG path data string.
181 \li \l PathCurve - a point on a Catmull-Rom curve.
182 \li \l PathAttribute - an attribute at a given position in the path.
183 \li \l PathPercent - a way to spread out items along various segments of the path.
186 \snippet qml/pathview/pathattributes.qml 2
189 QQmlListProperty<QQuickPathElement> QQuickPath::pathElements()
192 return QQmlListProperty<QQuickPathElement>(this, d->_pathElements);
195 void QQuickPath::interpolate(int idx, const QString &name, qreal value)
198 interpolate(d->_attributePoints, idx, name, value);
201 void QQuickPath::interpolate(QList<AttributePoint> &attributePoints, int idx, const QString &name, qreal value)
207 qreal lastPercent = 0;
208 int search = idx - 1;
210 const AttributePoint &point = attributePoints.at(search);
211 if (point.values.contains(name)) {
212 lastValue = point.values.value(name);
213 lastPercent = point.origpercent;
221 const AttributePoint &curPoint = attributePoints.at(idx);
223 for (int ii = search; ii < idx; ++ii) {
224 AttributePoint &point = attributePoints[ii];
226 qreal val = lastValue + (value - lastValue) * (point.origpercent - lastPercent) / (curPoint.origpercent - lastPercent);
227 point.values.insert(name, val);
231 void QQuickPath::endpoint(const QString &name)
234 const AttributePoint &first = d->_attributePoints.first();
235 qreal val = first.values.value(name);
236 for (int ii = d->_attributePoints.count() - 1; ii >= 0; ii--) {
237 const AttributePoint &point = d->_attributePoints.at(ii);
238 if (point.values.contains(name)) {
239 for (int jj = ii + 1; jj < d->_attributePoints.count(); ++jj) {
240 AttributePoint &setPoint = d->_attributePoints[jj];
241 setPoint.values.insert(name, val);
248 void QQuickPath::endpoint(QList<AttributePoint> &attributePoints, const QString &name)
250 const AttributePoint &first = attributePoints.first();
251 qreal val = first.values.value(name);
252 for (int ii = attributePoints.count() - 1; ii >= 0; ii--) {
253 const AttributePoint &point = attributePoints.at(ii);
254 if (point.values.contains(name)) {
255 for (int jj = ii + 1; jj < attributePoints.count(); ++jj) {
256 AttributePoint &setPoint = attributePoints[jj];
257 setPoint.values.insert(name, val);
264 static QString percentString(QLatin1String("_qfx_percent"));
266 void QQuickPath::processPath()
270 if (!d->componentComplete)
273 d->_pointCache.clear();
274 d->prevBez.isValid = false;
276 d->_path = createPath(QPointF(), QPointF(), d->_attributes, d->pathLength, d->_attributePoints, &d->closed);
281 QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &endPoint, const QStringList &attributes, qreal &pathLength, QList<AttributePoint> &attributePoints, bool *closed)
286 attributePoints.clear();
288 if (!d->componentComplete)
289 return QPainterPath();
293 AttributePoint first;
294 for (int ii = 0; ii < attributes.count(); ++ii)
295 first.values[attributes.at(ii)] = 0;
296 attributePoints << first;
298 qreal startX = d->startX.isValid() ? d->startX.value : startPoint.x();
299 qreal startY = d->startY.isValid() ? d->startY.value : startPoint.y();
300 path.moveTo(startX, startY);
302 bool usesPercent = false;
304 foreach (QQuickPathElement *pathElement, d->_pathElements) {
305 if (QQuickCurve *curve = qobject_cast<QQuickCurve *>(pathElement)) {
308 data.endPoint = endPoint;
309 data.curves = d->_pathCurves;
310 curve->addToPath(path, data);
312 p.origpercent = path.length();
313 attributePoints << p;
315 } else if (QQuickPathAttribute *attribute = qobject_cast<QQuickPathAttribute *>(pathElement)) {
316 AttributePoint &point = attributePoints.last();
317 point.values[attribute->name()] = attribute->value();
318 interpolate(attributePoints, attributePoints.count() - 1, attribute->name(), attribute->value());
319 } else if (QQuickPathPercent *percent = qobject_cast<QQuickPathPercent *>(pathElement)) {
320 AttributePoint &point = attributePoints.last();
321 point.values[percentString] = percent->value();
322 interpolate(attributePoints, attributePoints.count() - 1, percentString, percent->value());
328 const AttributePoint &last = attributePoints.last();
329 for (int ii = 0; ii < attributes.count(); ++ii) {
330 if (!last.values.contains(attributes.at(ii)))
331 endpoint(attributePoints, attributes.at(ii));
333 if (usesPercent && !last.values.contains(percentString)) {
334 d->_attributePoints.last().values[percentString] = 1;
335 interpolate(d->_attributePoints.count() - 1, percentString, 1);
340 qreal length = path.length();
341 qreal prevpercent = 0;
342 qreal prevorigpercent = 0;
343 for (int ii = 0; ii < attributePoints.count(); ++ii) {
344 const AttributePoint &point = attributePoints.at(ii);
345 if (point.values.contains(percentString)) { //special string for QQuickPathPercent
347 qreal scale = (attributePoints[ii].origpercent/length - prevorigpercent) /
348 (point.values.value(percentString)-prevpercent);
349 attributePoints[ii].scale = scale;
351 attributePoints[ii].origpercent /= length;
352 attributePoints[ii].percent = point.values.value(percentString);
353 prevorigpercent = attributePoints[ii].origpercent;
354 prevpercent = attributePoints[ii].percent;
356 attributePoints[ii].origpercent /= length;
357 attributePoints[ii].percent = attributePoints[ii].origpercent;
362 QPointF end = path.currentPosition();
363 *closed = length > 0 && startX == end.x() && startY == end.y();
370 void QQuickPath::classBegin()
373 d->componentComplete = false;
376 void QQuickPath::componentComplete()
380 d->componentComplete = true;
382 // First gather up all the attributes
383 foreach (QQuickPathElement *pathElement, d->_pathElements) {
384 if (QQuickCurve *curve =
385 qobject_cast<QQuickCurve *>(pathElement))
386 d->_pathCurves.append(curve);
387 else if (QQuickPathAttribute *attribute =
388 qobject_cast<QQuickPathAttribute *>(pathElement))
389 attrs.insert(attribute->name());
391 d->_attributes = attrs.toList();
395 foreach (QQuickPathElement *pathElement, d->_pathElements)
396 connect(pathElement, SIGNAL(changed()), this, SLOT(processPath()));
399 QPainterPath QQuickPath::path() const
401 Q_D(const QQuickPath);
405 QStringList QQuickPath::attributes() const
407 Q_D(const QQuickPath);
408 if (!d->componentComplete) {
411 // First gather up all the attributes
412 foreach (QQuickPathElement *pathElement, d->_pathElements) {
413 if (QQuickPathAttribute *attribute =
414 qobject_cast<QQuickPathAttribute *>(pathElement))
415 attrs.insert(attribute->name());
417 return attrs.toList();
419 return d->_attributes;
422 static inline QBezier nextBezier(const QPainterPath &path, int *current, qreal *bezLength, bool reverse = false)
424 const int lastElement = reverse ? 0 : path.elementCount() - 1;
425 const int start = reverse ? *current - 1 : *current + 1;
426 for (int i=start; reverse ? i >= lastElement : i <= lastElement; reverse ? --i : ++i) {
427 const QPainterPath::Element &e = path.elementAt(i);
430 case QPainterPath::MoveToElement:
432 case QPainterPath::LineToElement:
434 QLineF line(path.elementAt(i-1), e);
435 *bezLength = line.length();
436 QPointF a = path.elementAt(i-1);
437 QPointF delta = e - a;
439 return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
441 case QPainterPath::CurveToElement:
443 QBezier b = QBezier::fromPoints(path.elementAt(i-1),
446 path.elementAt(i+2));
447 *bezLength = b.length();
455 *current = lastElement;
460 static inline int segmentCount(const QPainterPath &path, qreal pathLength)
462 // In the really simple case of a single straight line we can interpolate without jitter
463 // between just two points.
464 if (path.elementCount() == 2
465 && path.elementAt(0).type == QPainterPath::MoveToElement
466 && path.elementAt(1).type == QPainterPath::LineToElement) {
469 // more points means less jitter between items as they move along the
470 // path, but takes longer to generate
471 return qCeil(pathLength*5);
474 //derivative of the equation
475 static inline qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d)
477 return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
480 void QQuickPath::createPointCache() const
482 Q_D(const QQuickPath);
483 qreal pathLength = d->pathLength;
484 if (pathLength <= 0 || qIsNaN(pathLength))
487 const int segments = segmentCount(d->_path, pathLength);
488 const int lastElement = d->_path.elementCount() - 1;
489 d->_pointCache.resize(segments+1);
491 int currElement = -1;
493 QBezier currBez = nextBezier(d->_path, &currElement, &bezLength);
494 qreal currLength = bezLength;
495 qreal epc = currLength / pathLength;
497 for (int i = 0; i < d->_pointCache.size(); i++) {
498 //find which set we are in
499 qreal prevPercent = 0;
500 qreal prevOrigPercent = 0;
501 for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
502 qreal percent = qreal(i)/segments;
503 const AttributePoint &point = d->_attributePoints.at(ii);
504 if (percent < point.percent || ii == d->_attributePoints.count() - 1) { //### || is special case for very last item
505 qreal elementPercent = (percent - prevPercent);
507 qreal spc = prevOrigPercent + elementPercent * point.scale;
510 if (currElement > lastElement)
512 currBez = nextBezier(d->_path, &currElement, &bezLength);
513 if (bezLength == 0.0) {
514 currLength = pathLength;
518 currLength += bezLength;
519 epc = currLength / pathLength;
521 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
522 d->_pointCache[i] = currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
525 prevOrigPercent = point.origpercent;
526 prevPercent = point.percent;
531 void QQuickPath::invalidateSequentialHistory() const
533 Q_D(const QQuickPath);
534 d->prevBez.isValid = false;
537 QPointF QQuickPath::sequentialPointAt(qreal p, qreal *angle) const
539 Q_D(const QQuickPath);
540 return sequentialPointAt(d->_path, d->pathLength, d->_attributePoints, d->prevBez, p, angle);
543 QPointF QQuickPath::sequentialPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
545 Q_ASSERT(p >= 0.0 && p <= 1.0);
547 if (!prevBez.isValid)
548 return p > .5 ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
549 forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
551 return p < prevBez.p ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
552 forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
555 QPointF QQuickPath::forwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
557 if (pathLength <= 0 || qIsNaN(pathLength))
558 return path.pointAtPercent(0); //expensive?
560 const int lastElement = path.elementCount() - 1;
561 bool haveCachedBez = prevBez.isValid;
562 int currElement = haveCachedBez ? prevBez.element : -1;
563 qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
564 QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength);
565 qreal currLength = haveCachedBez ? prevBez.currLength : bezLength;
566 qreal epc = currLength / pathLength;
568 //find which set we are in
569 qreal prevPercent = 0;
570 qreal prevOrigPercent = 0;
571 for (int ii = 0; ii < attributePoints.count(); ++ii) {
573 const AttributePoint &point = attributePoints.at(ii);
574 if (percent < point.percent || ii == attributePoints.count() - 1) {
575 qreal elementPercent = (percent - prevPercent);
577 qreal spc = prevOrigPercent + elementPercent * point.scale;
580 Q_ASSERT(!(currElement > lastElement));
581 Q_UNUSED(lastElement);
582 currBez = nextBezier(path, &currElement, &bezLength);
583 currLength += bezLength;
584 epc = currLength / pathLength;
586 prevBez.element = currElement;
587 prevBez.bezLength = bezLength;
588 prevBez.currLength = currLength;
589 prevBez.bezier = currBez;
591 prevBez.isValid = true;
593 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
596 qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
597 qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
598 *angle = QLineF(0, 0, m1, m2).angle();
601 return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
603 prevOrigPercent = point.origpercent;
604 prevPercent = point.percent;
610 //ideally this should be merged with forwardsPointAt
611 QPointF QQuickPath::backwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
613 if (pathLength <= 0 || qIsNaN(pathLength))
614 return path.pointAtPercent(0);
616 const int firstElement = 1; //element 0 is always a MoveTo, which we ignore
617 bool haveCachedBez = prevBez.isValid;
618 int currElement = haveCachedBez ? prevBez.element : path.elementCount();
619 qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
620 QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength, true /*reverse*/);
621 qreal currLength = haveCachedBez ? prevBez.currLength : pathLength;
622 qreal prevLength = currLength - bezLength;
623 qreal epc = prevLength / pathLength;
625 for (int ii = attributePoints.count() - 1; ii > 0; --ii) {
627 const AttributePoint &point = attributePoints.at(ii);
628 const AttributePoint &prevPoint = attributePoints.at(ii-1);
629 if (percent > prevPoint.percent || ii == 1) {
630 qreal elementPercent = (percent - prevPoint.percent);
632 qreal spc = prevPoint.origpercent + elementPercent * point.scale;
635 Q_ASSERT(!(currElement < firstElement));
636 Q_UNUSED(firstElement);
637 currBez = nextBezier(path, &currElement, &bezLength, true /*reverse*/);
638 //special case for first element is to avoid floating point math
639 //causing an epc that never hits 0.
640 currLength = (currElement == firstElement) ? bezLength : prevLength;
641 prevLength = currLength - bezLength;
642 epc = prevLength / pathLength;
644 prevBez.element = currElement;
645 prevBez.bezLength = bezLength;
646 prevBez.currLength = currLength;
647 prevBez.bezier = currBez;
649 prevBez.isValid = true;
651 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
654 qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
655 qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
656 *angle = QLineF(0, 0, m1, m2).angle();
659 return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
666 QPointF QQuickPath::pointAt(qreal p) const
668 Q_D(const QQuickPath);
669 if (d->_pointCache.isEmpty()) {
671 if (d->_pointCache.isEmpty())
675 const int segmentCount = d->_pointCache.size() - 1;
676 qreal idxf = p*segmentCount;
677 int idx1 = qFloor(idxf);
678 qreal delta = idxf - idx1;
679 if (idx1 > segmentCount)
685 return d->_pointCache.at(idx1);
687 // interpolate between the two points.
688 int idx2 = qCeil(idxf);
689 if (idx2 > segmentCount)
694 QPointF p1 = d->_pointCache.at(idx1);
695 QPointF p2 = d->_pointCache.at(idx2);
696 QPointF pos = p1 * (1.0-delta) + p2 * delta;
701 qreal QQuickPath::attributeAt(const QString &name, qreal percent) const
703 Q_D(const QQuickPath);
704 if (percent < 0 || percent > 1)
707 for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
708 const AttributePoint &point = d->_attributePoints.at(ii);
710 if (point.percent == percent) {
711 return point.values.value(name);
712 } else if (point.percent > percent) {
714 ii?(d->_attributePoints.at(ii - 1).values.value(name)):0;
716 ii?(d->_attributePoints.at(ii - 1).percent):0;
717 qreal curValue = point.values.value(name);
718 qreal curPercent = point.percent;
720 return lastValue + (curValue - lastValue) * (percent - lastPercent) / (curPercent - lastPercent);
727 /****************************************************************************/
729 qreal QQuickCurve::x() const
731 return _x.isNull ? 0 : _x.value;
734 void QQuickCurve::setX(qreal x)
736 if (_x.isNull || _x != x) {
743 bool QQuickCurve::hasX()
748 qreal QQuickCurve::y() const
750 return _y.isNull ? 0 : _y.value;
753 void QQuickCurve::setY(qreal y)
755 if (_y.isNull || _y != y) {
762 bool QQuickCurve::hasY()
767 qreal QQuickCurve::relativeX() const
772 void QQuickCurve::setRelativeX(qreal x)
774 if (_relativeX.isNull || _relativeX != x) {
776 emit relativeXChanged();
781 bool QQuickCurve::hasRelativeX()
783 return _relativeX.isValid();
786 qreal QQuickCurve::relativeY() const
791 void QQuickCurve::setRelativeY(qreal y)
793 if (_relativeY.isNull || _relativeY != y) {
795 emit relativeYChanged();
800 bool QQuickCurve::hasRelativeY()
802 return _relativeY.isValid();
805 /****************************************************************************/
808 \qmltype PathAttribute
809 \instantiates QQuickPathAttribute
810 \inqmlmodule QtQuick 2
811 \ingroup qtquick-animation-paths
812 \brief Specifies how to set an attribute at a given position in a Path
814 The PathAttribute object allows attributes consisting of a name and
815 a value to be specified for various points along a path. The
816 attributes are exposed to the delegate as
817 \l{Attached Properties and Attached Signal Handlers} {Attached Properties}.
818 The value of an attribute at any particular point along the path is interpolated
819 from the PathAttributes bounding that point.
821 The example below shows a path with the items scaled to 30% with
822 opacity 50% at the top of the path and scaled 100% with opacity
823 100% at the bottom. Note the use of the PathView.iconScale and
824 PathView.iconOpacity attached properties to set the scale and opacity
829 \li \image declarative-pathattribute.png
831 \snippet qml/pathview/pathattributes.qml 0
832 (see the PathView documentation for the specification of ContactModel.qml
833 used for ContactModel above.)
841 \qmlproperty string QtQuick2::PathAttribute::name
842 This property holds the name of the attribute to change.
844 This attribute will be available to the delegate as PathView.<name>
846 Note that using an existing Item property name such as "opacity" as an
847 attribute is allowed. This is because path attributes add a new
848 \l{Attached Properties and Attached Signal Handlers} {Attached Property}
849 which in no way clashes with existing properties.
853 the name of the attribute to change.
856 QString QQuickPathAttribute::name() const
861 void QQuickPathAttribute::setName(const QString &name)
870 \qmlproperty real QtQuick2::PathAttribute::value
871 This property holds the value for the attribute.
873 The value specified can be used to influence the visual appearance
874 of an item along the path. For example, the following Path specifies
875 an attribute named \e itemRotation, which has the value \e 0 at the
876 beginning of the path, and the value 90 at the end of the path.
882 PathAttribute { name: "itemRotation"; value: 0 }
883 PathLine { x: 100; y: 100 }
884 PathAttribute { name: "itemRotation"; value: 90 }
888 In our delegate, we can then bind the \e rotation property to the
889 \l{Attached Properties and Attached Signal Handlers} {Attached Property}
890 \e PathView.itemRotation created for this attribute.
894 width: 10; height: 10
895 rotation: PathView.itemRotation
899 As each item is positioned along the path, it will be rotated accordingly:
900 an item at the beginning of the path with be not be rotated, an item at
901 the end of the path will be rotated 90 degrees, and an item mid-way along
902 the path will be rotated 45 degrees.
906 the new value of the attribute.
908 qreal QQuickPathAttribute::value() const
913 void QQuickPathAttribute::setValue(qreal value)
915 if (_value != value) {
922 /****************************************************************************/
926 \instantiates QQuickPathLine
927 \inqmlmodule QtQuick 2
928 \ingroup qtquick-animation-paths
929 \brief Defines a straight line
931 The example below creates a path consisting of a straight line from
936 startX: 0; startY: 100
937 PathLine { x: 200; y: 100 }
941 \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
945 \qmlproperty real QtQuick2::PathLine::x
946 \qmlproperty real QtQuick2::PathLine::y
948 Defines the end point of the line.
950 \sa relativeX, relativeY
954 \qmlproperty real QtQuick2::PathLine::relativeX
955 \qmlproperty real QtQuick2::PathLine::relativeY
957 Defines the end point of the line relative to its start.
959 If both a relative and absolute end position are specified for a single axis, the relative
960 position will be used.
962 Relative and absolute positions can be mixed, for example it is valid to set a relative x
968 inline QPointF positionForCurve(const QQuickPathData &data, const QPointF &prevPoint)
970 QQuickCurve *curve = data.curves.at(data.index);
971 bool isEnd = data.index == data.curves.size() - 1;
972 return QPointF(curve->hasRelativeX() ? prevPoint.x() + curve->relativeX() : !isEnd || curve->hasX() ? curve->x() : data.endPoint.x(),
973 curve->hasRelativeY() ? prevPoint.y() + curve->relativeY() : !isEnd || curve->hasY() ? curve->y() : data.endPoint.y());
976 void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data)
978 path.lineTo(positionForCurve(data, path.currentPosition()));
981 /****************************************************************************/
985 \instantiates QQuickPathQuad
986 \inqmlmodule QtQuick 2
987 \ingroup qtquick-animation-paths
988 \brief Defines a quadratic Bezier curve with a control point
990 The following QML produces the path shown below:
993 \li \image declarative-pathquad.png
998 PathQuad { x: 200; y: 0; controlX: 100; controlY: 150 }
1003 \sa Path, PathCubic, PathLine, PathArc, PathCurve, PathSvg
1007 \qmlproperty real QtQuick2::PathQuad::x
1008 \qmlproperty real QtQuick2::PathQuad::y
1010 Defines the end point of the curve.
1012 \sa relativeX, relativeY
1016 \qmlproperty real QtQuick2::PathQuad::relativeX
1017 \qmlproperty real QtQuick2::PathQuad::relativeY
1019 Defines the end point of the curve relative to its start.
1021 If both a relative and absolute end position are specified for a single axis, the relative
1022 position will be used.
1024 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1031 \qmlproperty real QtQuick2::PathQuad::controlX
1032 \qmlproperty real QtQuick2::PathQuad::controlY
1034 Defines the position of the control point.
1038 the x position of the control point.
1040 qreal QQuickPathQuad::controlX() const
1045 void QQuickPathQuad::setControlX(qreal x)
1047 if (_controlX != x) {
1049 emit controlXChanged();
1056 the y position of the control point.
1058 qreal QQuickPathQuad::controlY() const
1063 void QQuickPathQuad::setControlY(qreal y)
1065 if (_controlY != y) {
1067 emit controlYChanged();
1073 \qmlproperty real QtQuick2::PathQuad::relativeControlX
1074 \qmlproperty real QtQuick2::PathQuad::relativeControlY
1076 Defines the position of the control point relative to the curve's start.
1078 If both a relative and absolute control position are specified for a single axis, the relative
1079 position will be used.
1081 Relative and absolute positions can be mixed, for example it is valid to set a relative control x
1082 and an absolute control y.
1084 \sa controlX, controlY
1087 qreal QQuickPathQuad::relativeControlX() const
1089 return _relativeControlX;
1092 void QQuickPathQuad::setRelativeControlX(qreal x)
1094 if (_relativeControlX.isNull || _relativeControlX != x) {
1095 _relativeControlX = x;
1096 emit relativeControlXChanged();
1101 bool QQuickPathQuad::hasRelativeControlX()
1103 return _relativeControlX.isValid();
1106 qreal QQuickPathQuad::relativeControlY() const
1108 return _relativeControlY;
1111 void QQuickPathQuad::setRelativeControlY(qreal y)
1113 if (_relativeControlY.isNull || _relativeControlY != y) {
1114 _relativeControlY = y;
1115 emit relativeControlYChanged();
1120 bool QQuickPathQuad::hasRelativeControlY()
1122 return _relativeControlY.isValid();
1125 void QQuickPathQuad::addToPath(QPainterPath &path, const QQuickPathData &data)
1127 const QPointF &prevPoint = path.currentPosition();
1128 QPointF controlPoint(hasRelativeControlX() ? prevPoint.x() + relativeControlX() : controlX(),
1129 hasRelativeControlY() ? prevPoint.y() + relativeControlY() : controlY());
1130 path.quadTo(controlPoint, positionForCurve(data, path.currentPosition()));
1133 /****************************************************************************/
1137 \instantiates QQuickPathCubic
1138 \inqmlmodule QtQuick 2
1139 \ingroup qtquick-animation-paths
1140 \brief Defines a cubic Bezier curve with two control points
1142 The following QML produces the path shown below:
1145 \li \image declarative-pathcubic.png
1149 startX: 20; startY: 0
1152 control1X: -10; control1Y: 90
1153 control2X: 210; control2Y: 90
1159 \sa Path, PathQuad, PathLine, PathArc, PathCurve, PathSvg
1163 \qmlproperty real QtQuick2::PathCubic::x
1164 \qmlproperty real QtQuick2::PathCubic::y
1166 Defines the end point of the curve.
1168 \sa relativeX, relativeY
1172 \qmlproperty real QtQuick2::PathCubic::relativeX
1173 \qmlproperty real QtQuick2::PathCubic::relativeY
1175 Defines the end point of the curve relative to its start.
1177 If both a relative and absolute end position are specified for a single axis, the relative
1178 position will be used.
1180 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1187 \qmlproperty real QtQuick2::PathCubic::control1X
1188 \qmlproperty real QtQuick2::PathCubic::control1Y
1190 Defines the position of the first control point.
1192 qreal QQuickPathCubic::control1X() const
1197 void QQuickPathCubic::setControl1X(qreal x)
1199 if (_control1X != x) {
1201 emit control1XChanged();
1206 qreal QQuickPathCubic::control1Y() const
1211 void QQuickPathCubic::setControl1Y(qreal y)
1213 if (_control1Y != y) {
1215 emit control1YChanged();
1221 \qmlproperty real QtQuick2::PathCubic::control2X
1222 \qmlproperty real QtQuick2::PathCubic::control2Y
1224 Defines the position of the second control point.
1226 qreal QQuickPathCubic::control2X() const
1231 void QQuickPathCubic::setControl2X(qreal x)
1233 if (_control2X != x) {
1235 emit control2XChanged();
1240 qreal QQuickPathCubic::control2Y() const
1245 void QQuickPathCubic::setControl2Y(qreal y)
1247 if (_control2Y != y) {
1249 emit control2YChanged();
1255 \qmlproperty real QtQuick2::PathCubic::relativeControl1X
1256 \qmlproperty real QtQuick2::PathCubic::relativeControl1Y
1257 \qmlproperty real QtQuick2::PathCubic::relativeControl2X
1258 \qmlproperty real QtQuick2::PathCubic::relativeControl2Y
1260 Defines the positions of the control points relative to the curve's start.
1262 If both a relative and absolute control position are specified for a control point's axis, the relative
1263 position will be used.
1265 Relative and absolute positions can be mixed, for example it is valid to set a relative control1 x
1266 and an absolute control1 y.
1268 \sa control1X, control1Y, control2X, control2Y
1271 qreal QQuickPathCubic::relativeControl1X() const
1273 return _relativeControl1X;
1276 void QQuickPathCubic::setRelativeControl1X(qreal x)
1278 if (_relativeControl1X.isNull || _relativeControl1X != x) {
1279 _relativeControl1X = x;
1280 emit relativeControl1XChanged();
1285 bool QQuickPathCubic::hasRelativeControl1X()
1287 return _relativeControl1X.isValid();
1290 qreal QQuickPathCubic::relativeControl1Y() const
1292 return _relativeControl1Y;
1295 void QQuickPathCubic::setRelativeControl1Y(qreal y)
1297 if (_relativeControl1Y.isNull || _relativeControl1Y != y) {
1298 _relativeControl1Y = y;
1299 emit relativeControl1YChanged();
1304 bool QQuickPathCubic::hasRelativeControl1Y()
1306 return _relativeControl1Y.isValid();
1309 qreal QQuickPathCubic::relativeControl2X() const
1311 return _relativeControl2X;
1314 void QQuickPathCubic::setRelativeControl2X(qreal x)
1316 if (_relativeControl2X.isNull || _relativeControl2X != x) {
1317 _relativeControl2X = x;
1318 emit relativeControl2XChanged();
1323 bool QQuickPathCubic::hasRelativeControl2X()
1325 return _relativeControl2X.isValid();
1328 qreal QQuickPathCubic::relativeControl2Y() const
1330 return _relativeControl2Y;
1333 void QQuickPathCubic::setRelativeControl2Y(qreal y)
1335 if (_relativeControl2Y.isNull || _relativeControl2Y != y) {
1336 _relativeControl2Y = y;
1337 emit relativeControl2YChanged();
1342 bool QQuickPathCubic::hasRelativeControl2Y()
1344 return _relativeControl2Y.isValid();
1347 void QQuickPathCubic::addToPath(QPainterPath &path, const QQuickPathData &data)
1349 const QPointF &prevPoint = path.currentPosition();
1350 QPointF controlPoint1(hasRelativeControl1X() ? prevPoint.x() + relativeControl1X() : control1X(),
1351 hasRelativeControl1Y() ? prevPoint.y() + relativeControl1Y() : control1Y());
1352 QPointF controlPoint2(hasRelativeControl2X() ? prevPoint.x() + relativeControl2X() : control2X(),
1353 hasRelativeControl2Y() ? prevPoint.y() + relativeControl2Y() : control2Y());
1354 path.cubicTo(controlPoint1, controlPoint2, positionForCurve(data, path.currentPosition()));
1357 /****************************************************************************/
1361 \instantiates QQuickPathCatmullRomCurve
1362 \inqmlmodule QtQuick 2
1363 \ingroup qtquick-animation-paths
1364 \brief Defines a point on a Catmull-Rom curve
1366 PathCurve provides an easy way to specify a curve passing directly through a set of points.
1367 Typically multiple PathCurves are used in a series, as the following example demonstrates:
1369 \snippet qml/path/basiccurve.qml 0
1371 This example produces the following path (with the starting point and PathCurve points
1372 highlighted in red):
1374 \image declarative-pathcurve.png
1376 \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathSvg
1380 \qmlproperty real QtQuick2::PathCurve::x
1381 \qmlproperty real QtQuick2::PathCurve::y
1383 Defines the end point of the curve.
1385 \sa relativeX, relativeY
1389 \qmlproperty real QtQuick2::PathCurve::relativeX
1390 \qmlproperty real QtQuick2::PathCurve::relativeY
1392 Defines the end point of the curve relative to its start.
1394 If both a relative and absolute end position are specified for a single axis, the relative
1395 position will be used.
1397 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1403 inline QPointF previousPathPosition(const QPainterPath &path)
1405 int count = path.elementCount();
1409 int index = path.elementAt(count-1).type == QPainterPath::CurveToDataElement ? count - 4 : count - 2;
1410 return index > -1 ? QPointF(path.elementAt(index)) : path.pointAtPercent(0);
1413 void QQuickPathCatmullRomCurve::addToPath(QPainterPath &path, const QQuickPathData &data)
1415 //here we convert catmull-rom spline to bezier for use in QPainterPath.
1416 //basic conversion algorithm:
1417 // catmull-rom points * inverse bezier matrix * catmull-rom matrix = bezier points
1418 //each point in the catmull-rom spline produces a bezier endpoint + 2 control points
1419 //calculations for each point use a moving window of 4 points
1420 // (previous 2 points + current point + next point)
1421 QPointF prevFar, prev, point, next;
1423 //get previous points
1424 int index = data.index - 1;
1425 QQuickCurve *curve = index == -1 ? 0 : data.curves.at(index);
1426 if (qobject_cast<QQuickPathCatmullRomCurve*>(curve)) {
1427 prev = path.currentPosition();
1428 prevFar = previousPathPosition(path);
1430 prev = path.currentPosition();
1431 bool prevFarSet = false;
1432 if (index == -1 && data.curves.count() > 1) {
1433 if (qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(data.curves.count()-1))) {
1434 //TODO: profile and optimize
1436 QQuickPathData loopData;
1437 loopData.endPoint = data.endPoint;
1438 loopData.curves = data.curves;
1439 for (int i = data.index; i < data.curves.count(); ++i) {
1441 pos = positionForCurve(loopData, pos);
1442 if (i == data.curves.count()-2)
1445 if (pos == QPointF(path.elementAt(0))) {
1446 //this is a closed path starting and ending with catmull-rom segments.
1447 //we try to smooth the join point
1457 point = positionForCurve(data, path.currentPosition());
1460 index = data.index + 1;
1461 if (index < data.curves.count() && qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(index))) {
1462 QQuickPathData nextData;
1463 nextData.index = index;
1464 nextData.endPoint = data.endPoint;
1465 nextData.curves = data.curves;
1466 next = positionForCurve(nextData, point);
1468 if (point == QPointF(path.elementAt(0)) && qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(0))) {
1469 //this is a closed path starting and ending with catmull-rom segments.
1470 //we try to smooth the join point
1471 next = QPointF(path.elementAt(3)); //the first catmull-rom point
1477 full conversion matrix (inverse bezier * catmull-rom):
1478 0.000, 1.000, 0.000, 0.000,
1479 -0.167, 1.000, 0.167, 0.000,
1480 0.000, 0.167, 1.000, -0.167,
1481 0.000, 0.000, 1.000, 0.000
1483 conversion doesn't require full matrix multiplication,
1484 so below we simplify
1486 QPointF control1(prevFar.x() * qreal(-0.167) +
1488 point.x() * qreal(0.167),
1489 prevFar.y() * qreal(-0.167) +
1491 point.y() * qreal(0.167));
1493 QPointF control2(prev.x() * qreal(0.167) +
1495 next.x() * qreal(-0.167),
1496 prev.y() * qreal(0.167) +
1498 next.y() * qreal(-0.167));
1500 path.cubicTo(control1, control2, point);
1503 /****************************************************************************/
1507 \instantiates QQuickPathArc
1508 \inqmlmodule QtQuick 2
1509 \ingroup qtquick-animation-paths
1510 \brief Defines an arc with the given radius
1512 PathArc provides a simple way of specifying an arc that ends at a given position
1513 and uses the specified radius. It is modeled after the SVG elliptical arc command.
1515 The following QML produces the path shown below:
1518 \li \image declarative-patharc.png
1519 \li \snippet qml/path/basicarc.qml 0
1522 Note that a single PathArc cannot be used to specify a circle. Instead, you can
1523 use two PathArc elements, each specifying half of the circle.
1525 \sa Path, PathLine, PathQuad, PathCubic, PathCurve, PathSvg
1529 \qmlproperty real QtQuick2::PathArc::x
1530 \qmlproperty real QtQuick2::PathArc::y
1532 Defines the end point of the arc.
1534 \sa relativeX, relativeY
1538 \qmlproperty real QtQuick2::PathArc::relativeX
1539 \qmlproperty real QtQuick2::PathArc::relativeY
1541 Defines the end point of the arc relative to its start.
1543 If both a relative and absolute end position are specified for a single axis, the relative
1544 position will be used.
1546 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1553 \qmlproperty real QtQuick2::PathArc::radiusX
1554 \qmlproperty real QtQuick2::PathArc::radiusY
1556 Defines the radius of the arc.
1558 The following QML demonstrates how different radius values can be used to change
1559 the shape of the arc:
1562 \li \image declarative-arcradius.png
1563 \li \snippet qml/path/arcradius.qml 0
1567 qreal QQuickPathArc::radiusX() const
1572 void QQuickPathArc::setRadiusX(qreal radius)
1574 if (_radiusX == radius)
1578 emit radiusXChanged();
1581 qreal QQuickPathArc::radiusY() const
1586 void QQuickPathArc::setRadiusY(qreal radius)
1588 if (_radiusY == radius)
1592 emit radiusYChanged();
1596 \qmlproperty bool QtQuick2::PathArc::useLargeArc
1597 Whether to use a large arc as defined by the arc points.
1599 Given fixed start and end positions, radius, and direction,
1600 there are two possible arcs that can fit the data. useLargeArc
1601 is used to distinguish between these. For example, the following
1602 QML can produce either of the two illustrated arcs below by
1603 changing the value of useLargeArc.
1607 \li \image declarative-largearc.png
1608 \li \snippet qml/path/largearc.qml 0
1611 The default value is false.
1614 bool QQuickPathArc::useLargeArc() const
1616 return _useLargeArc;
1619 void QQuickPathArc::setUseLargeArc(bool largeArc)
1621 if (_useLargeArc == largeArc)
1624 _useLargeArc = largeArc;
1625 emit useLargeArcChanged();
1629 \qmlproperty enumeration QtQuick2::PathArc::direction
1631 Defines the direction of the arc. Possible values are
1632 PathArc.Clockwise (default) and PathArc.Counterclockwise.
1634 The following QML can produce either of the two illustrated arcs below
1635 by changing the value of direction.
1638 \li \image declarative-arcdirection.png
1639 \li \snippet qml/path/arcdirection.qml 0
1645 QQuickPathArc::ArcDirection QQuickPathArc::direction() const
1650 void QQuickPathArc::setDirection(ArcDirection direction)
1652 if (_direction == direction)
1655 _direction = direction;
1656 emit directionChanged();
1659 void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data)
1661 const QPointF &startPoint = path.currentPosition();
1662 const QPointF &endPoint = positionForCurve(data, startPoint);
1663 QQuickSvgParser::pathArc(path,
1668 _direction == Clockwise ? 1 : 0,
1671 startPoint.x(), startPoint.y());
1674 /****************************************************************************/
1678 \instantiates QQuickPathSvg
1679 \inqmlmodule QtQuick 2
1680 \ingroup qtquick-animation-paths
1681 \brief Defines a path using an SVG path data string
1683 The following QML produces the path shown below:
1686 \li \image declarative-pathsvg.png
1690 startX: 50; startY: 50
1691 PathSvg { path: "L 150 50 L 100 150 z" }
1696 \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathCurve
1700 \qmlproperty string QtQuick2::PathSvg::path
1702 The SVG path data string specifying the path.
1704 See \l {http://www.w3.org/TR/SVG/paths.html#PathData}{W3C SVG Path Data}
1705 for more details on this format.
1708 QString QQuickPathSvg::path() const
1713 void QQuickPathSvg::setPath(const QString &path)
1722 void QQuickPathSvg::addToPath(QPainterPath &path, const QQuickPathData &)
1724 QQuickSvgParser::parsePathDataFast(_path, path);
1727 /****************************************************************************/
1730 \qmltype PathPercent
1731 \instantiates QQuickPathPercent
1732 \inqmlmodule QtQuick 2
1733 \ingroup qtquick-animation-paths
1734 \brief Manipulates the way a path is interpreted
1736 PathPercent allows you to manipulate the spacing between items on a
1737 PathView's path. You can use it to bunch together items on part of
1738 the path, and spread them out on other parts of the path.
1740 The examples below show the normal distribution of items along a path
1741 compared to a distribution which places 50% of the items along the
1742 PathLine section of the path.
1745 \li \image declarative-nopercent.png
1751 startX: 20; startY: 0
1752 PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
1753 PathLine { x: 150; y: 80 }
1754 PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
1759 \li \image declarative-percent.png
1765 startX: 20; startY: 0
1766 PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
1767 PathPercent { value: 0.25 }
1768 PathLine { x: 150; y: 80 }
1769 PathPercent { value: 0.75 }
1770 PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
1771 PathPercent { value: 1 }
1781 \qmlproperty real QtQuick2::PathPercent::value
1782 The proportion of items that should be laid out up to this point.
1784 This value should always be higher than the last value specified
1785 by a PathPercent at a previous position in the Path.
1787 In the following example we have a Path made up of three PathLines.
1788 Normally, the items of the PathView would be laid out equally along
1789 this path, with an equal number of items per line segment. PathPercent
1790 allows us to specify that the first and third lines should each hold
1791 10% of the laid out items, while the second line should hold the remaining
1798 startX: 0; startY: 0
1799 PathLine { x:100; y: 0; }
1800 PathPercent { value: 0.1 }
1801 PathLine { x: 100; y: 100 }
1802 PathPercent { value: 0.9 }
1803 PathLine { x: 100; y: 0 }
1804 PathPercent { value: 1 }
1810 qreal QQuickPathPercent::value() const
1815 void QQuickPathPercent::setValue(qreal value)
1817 if (_value != value) {
1819 emit valueChanged();