aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/util/qdeclarativepath.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/util/qdeclarativepath.cpp')
-rw-r--r--src/quick/util/qdeclarativepath.cpp1484
1 files changed, 1484 insertions, 0 deletions
diff --git a/src/quick/util/qdeclarativepath.cpp b/src/quick/util/qdeclarativepath.cpp
new file mode 100644
index 0000000000..2ba7c372e2
--- /dev/null
+++ b/src/quick/util/qdeclarativepath.cpp
@@ -0,0 +1,1484 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdeclarativepath_p.h"
+#include "qdeclarativepath_p_p.h"
+#include "qdeclarativesvgparser_p.h"
+
+#include <QSet>
+#include <QTime>
+
+#include <private/qbezier_p.h>
+#include <QtCore/qmath.h>
+#include <QtCore/qnumeric.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass PathElement QDeclarativePathElement
+ \inqmlmodule QtQuick 2
+ \ingroup qml-view-elements
+ \brief PathElement is the base path type.
+
+ This type is the base for all path types. It cannot
+ be instantiated.
+
+ \sa Path, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic
+*/
+
+/*!
+ \qmlclass Path QDeclarativePath
+ \inqmlmodule QtQuick 2
+ \ingroup qml-view-elements
+ \brief A Path object defines a path for use by \l PathView.
+
+ A Path is composed of one or more path segments - PathLine, PathQuad,
+ PathCubic.
+
+ The spacing of the items along the Path can be adjusted via a
+ PathPercent object.
+
+ PathAttribute allows named attributes with values to be defined
+ along the path.
+
+ \sa PathView, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic
+*/
+QDeclarativePath::QDeclarativePath(QObject *parent)
+ : QObject(*(new QDeclarativePathPrivate), parent)
+{
+}
+
+QDeclarativePath::~QDeclarativePath()
+{
+}
+
+/*!
+ \qmlproperty real QtQuick2::Path::startX
+ \qmlproperty real QtQuick2::Path::startY
+ These properties hold the starting position of the path.
+*/
+qreal QDeclarativePath::startX() const
+{
+ Q_D(const QDeclarativePath);
+ return d->startX.isNull ? 0 : d->startX.value;
+}
+
+void QDeclarativePath::setStartX(qreal x)
+{
+ Q_D(QDeclarativePath);
+ if (d->startX.isValid() && qFuzzyCompare(x, d->startX))
+ return;
+ d->startX = x;
+ emit startXChanged();
+ processPath();
+}
+
+bool QDeclarativePath::hasStartX() const
+{
+ Q_D(const QDeclarativePath);
+ return d->startX.isValid();
+}
+
+qreal QDeclarativePath::startY() const
+{
+ Q_D(const QDeclarativePath);
+ return d->startY.isNull ? 0 : d->startY.value;
+}
+
+void QDeclarativePath::setStartY(qreal y)
+{
+ Q_D(QDeclarativePath);
+ if (d->startY.isValid() && qFuzzyCompare(y, d->startY))
+ return;
+ d->startY = y;
+ emit startYChanged();
+ processPath();
+}
+
+bool QDeclarativePath::hasStartY() const
+{
+ Q_D(const QDeclarativePath);
+ return d->startY.isValid();
+}
+
+/*!
+ \qmlproperty bool QtQuick2::Path::closed
+ This property holds whether the start and end of the path are identical.
+*/
+bool QDeclarativePath::isClosed() const
+{
+ Q_D(const QDeclarativePath);
+ return d->closed;
+}
+
+bool QDeclarativePath::hasEnd() const
+{
+ Q_D(const QDeclarativePath);
+ for (int i = d->_pathElements.count() - 1; i > -1; --i) {
+ if (QDeclarativeCurve *curve = qobject_cast<QDeclarativeCurve *>(d->_pathElements.at(i))) {
+ if ((!curve->hasX() && !curve->hasRelativeX()) || (!curve->hasY() && !curve->hasRelativeY()))
+ return false;
+ else
+ return true;
+ }
+ }
+ return hasStartX() && hasStartY();
+}
+
+/*!
+ \qmlproperty list<PathElement> QtQuick2::Path::pathElements
+ This property holds the objects composing the path.
+
+ \default
+
+ A path can contain the following path objects:
+ \list
+ \i \l PathLine - a straight line to a given position.
+ \i \l PathQuad - a quadratic Bezier curve to a given position with a control point.
+ \i \l PathCubic - a cubic Bezier curve to a given position with two control points.
+ \i \l PathAttribute - an attribute at a given position in the path.
+ \i \l PathPercent - a way to spread out items along various segments of the path.
+ \endlist
+
+ \snippet doc/src/snippets/declarative/pathview/pathattributes.qml 2
+*/
+
+QDeclarativeListProperty<QDeclarativePathElement> QDeclarativePath::pathElements()
+{
+ Q_D(QDeclarativePath);
+ return QDeclarativeListProperty<QDeclarativePathElement>(this, d->_pathElements);
+}
+
+void QDeclarativePath::interpolate(int idx, const QString &name, qreal value)
+{
+ Q_D(QDeclarativePath);
+ interpolate(d->_attributePoints, idx, name, value);
+}
+
+void QDeclarativePath::interpolate(QList<AttributePoint> &attributePoints, int idx, const QString &name, qreal value)
+{
+ if (!idx)
+ return;
+
+ qreal lastValue = 0;
+ qreal lastPercent = 0;
+ int search = idx - 1;
+ while(search >= 0) {
+ const AttributePoint &point = attributePoints.at(search);
+ if (point.values.contains(name)) {
+ lastValue = point.values.value(name);
+ lastPercent = point.origpercent;
+ break;
+ }
+ --search;
+ }
+
+ ++search;
+
+ const AttributePoint &curPoint = attributePoints.at(idx);
+
+ for (int ii = search; ii < idx; ++ii) {
+ AttributePoint &point = attributePoints[ii];
+
+ qreal val = lastValue + (value - lastValue) * (point.origpercent - lastPercent) / (curPoint.origpercent - lastPercent);
+ point.values.insert(name, val);
+ }
+}
+
+void QDeclarativePath::endpoint(const QString &name)
+{
+ Q_D(QDeclarativePath);
+ const AttributePoint &first = d->_attributePoints.first();
+ qreal val = first.values.value(name);
+ for (int ii = d->_attributePoints.count() - 1; ii >= 0; ii--) {
+ const AttributePoint &point = d->_attributePoints.at(ii);
+ if (point.values.contains(name)) {
+ for (int jj = ii + 1; jj < d->_attributePoints.count(); ++jj) {
+ AttributePoint &setPoint = d->_attributePoints[jj];
+ setPoint.values.insert(name, val);
+ }
+ return;
+ }
+ }
+}
+
+void QDeclarativePath::endpoint(QList<AttributePoint> &attributePoints, const QString &name)
+{
+ const AttributePoint &first = attributePoints.first();
+ qreal val = first.values.value(name);
+ for (int ii = attributePoints.count() - 1; ii >= 0; ii--) {
+ const AttributePoint &point = attributePoints.at(ii);
+ if (point.values.contains(name)) {
+ for (int jj = ii + 1; jj < attributePoints.count(); ++jj) {
+ AttributePoint &setPoint = attributePoints[jj];
+ setPoint.values.insert(name, val);
+ }
+ return;
+ }
+ }
+}
+
+static QString percentString(QLatin1String("_qfx_percent"));
+
+void QDeclarativePath::processPath()
+{
+ Q_D(QDeclarativePath);
+
+ if (!d->componentComplete)
+ return;
+
+ d->_pointCache.clear();
+ d->prevBez.isValid = false;
+
+ d->_path = createPath(QPointF(), QPointF(), d->_attributes, d->pathLength, d->_attributePoints, &d->closed);
+
+ emit changed();
+}
+
+QPainterPath QDeclarativePath::createPath(const QPointF &startPoint, const QPointF &endPoint, const QStringList &attributes, qreal &pathLength, QList<AttributePoint> &attributePoints, bool *closed)
+{
+ Q_D(QDeclarativePath);
+
+ pathLength = 0;
+ attributePoints.clear();
+
+ if (!d->componentComplete)
+ return QPainterPath();
+
+ QPainterPath path;
+
+ AttributePoint first;
+ for (int ii = 0; ii < attributes.count(); ++ii)
+ first.values[attributes.at(ii)] = 0;
+ attributePoints << first;
+
+ qreal startX = d->startX.isValid() ? d->startX.value : startPoint.x();
+ qreal startY = d->startY.isValid() ? d->startY.value : startPoint.y();
+ path.moveTo(startX, startY);
+
+ bool usesPercent = false;
+ int index = 0;
+ foreach (QDeclarativePathElement *pathElement, d->_pathElements) {
+ if (QDeclarativeCurve *curve = qobject_cast<QDeclarativeCurve *>(pathElement)) {
+ QDeclarativePathData data;
+ data.index = index;
+ data.endPoint = endPoint;
+ data.curves = d->_pathCurves;
+ curve->addToPath(path, data);
+ AttributePoint p;
+ p.origpercent = path.length();
+ attributePoints << p;
+ ++index;
+ } else if (QDeclarativePathAttribute *attribute = qobject_cast<QDeclarativePathAttribute *>(pathElement)) {
+ AttributePoint &point = attributePoints.last();
+ point.values[attribute->name()] = attribute->value();
+ interpolate(attributePoints, attributePoints.count() - 1, attribute->name(), attribute->value());
+ } else if (QDeclarativePathPercent *percent = qobject_cast<QDeclarativePathPercent *>(pathElement)) {
+ AttributePoint &point = attributePoints.last();
+ point.values[percentString] = percent->value();
+ interpolate(attributePoints, attributePoints.count() - 1, percentString, percent->value());
+ usesPercent = true;
+ }
+ }
+
+ // Fixup end points
+ const AttributePoint &last = attributePoints.last();
+ for (int ii = 0; ii < attributes.count(); ++ii) {
+ if (!last.values.contains(attributes.at(ii)))
+ endpoint(attributePoints, attributes.at(ii));
+ }
+ if (usesPercent && !last.values.contains(percentString)) {
+ d->_attributePoints.last().values[percentString] = 1;
+ interpolate(d->_attributePoints.count() - 1, percentString, 1);
+ }
+
+
+ // Adjust percent
+ qreal length = path.length();
+ qreal prevpercent = 0;
+ qreal prevorigpercent = 0;
+ for (int ii = 0; ii < attributePoints.count(); ++ii) {
+ const AttributePoint &point = attributePoints.at(ii);
+ if (point.values.contains(percentString)) { //special string for QDeclarativePathPercent
+ if ( ii > 0) {
+ qreal scale = (attributePoints[ii].origpercent/length - prevorigpercent) /
+ (point.values.value(percentString)-prevpercent);
+ attributePoints[ii].scale = scale;
+ }
+ attributePoints[ii].origpercent /= length;
+ attributePoints[ii].percent = point.values.value(percentString);
+ prevorigpercent = attributePoints[ii].origpercent;
+ prevpercent = attributePoints[ii].percent;
+ } else {
+ attributePoints[ii].origpercent /= length;
+ attributePoints[ii].percent = attributePoints[ii].origpercent;
+ }
+ }
+
+ if (closed) {
+ QPointF end = path.currentPosition();
+ *closed = length > 0 && startX == end.x() && startY == end.y();
+ }
+ pathLength = length;
+
+ return path;
+}
+
+void QDeclarativePath::classBegin()
+{
+ Q_D(QDeclarativePath);
+ d->componentComplete = false;
+}
+
+void QDeclarativePath::componentComplete()
+{
+ Q_D(QDeclarativePath);
+ QSet<QString> attrs;
+ d->componentComplete = true;
+
+ // First gather up all the attributes
+ foreach (QDeclarativePathElement *pathElement, d->_pathElements) {
+ if (QDeclarativeCurve *curve =
+ qobject_cast<QDeclarativeCurve *>(pathElement))
+ d->_pathCurves.append(curve);
+ else if (QDeclarativePathAttribute *attribute =
+ qobject_cast<QDeclarativePathAttribute *>(pathElement))
+ attrs.insert(attribute->name());
+ }
+ d->_attributes = attrs.toList();
+
+ processPath();
+
+ foreach (QDeclarativePathElement *pathElement, d->_pathElements)
+ connect(pathElement, SIGNAL(changed()), this, SLOT(processPath()));
+}
+
+QPainterPath QDeclarativePath::path() const
+{
+ Q_D(const QDeclarativePath);
+ return d->_path;
+}
+
+QStringList QDeclarativePath::attributes() const
+{
+ Q_D(const QDeclarativePath);
+ if (!d->componentComplete) {
+ QSet<QString> attrs;
+
+ // First gather up all the attributes
+ foreach (QDeclarativePathElement *pathElement, d->_pathElements) {
+ if (QDeclarativePathAttribute *attribute =
+ qobject_cast<QDeclarativePathAttribute *>(pathElement))
+ attrs.insert(attribute->name());
+ }
+ return attrs.toList();
+ }
+ return d->_attributes;
+}
+
+static inline QBezier nextBezier(const QPainterPath &path, int *current, qreal *bezLength, bool reverse = false)
+{
+ const int lastElement = reverse ? 0 : path.elementCount() - 1;
+ const int start = reverse ? *current - 1 : *current + 1;
+ for (int i=start; reverse ? i >= lastElement : i <= lastElement; reverse ? --i : ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ break;
+ case QPainterPath::LineToElement:
+ {
+ QLineF line(path.elementAt(i-1), e);
+ *bezLength = line.length();
+ QPointF a = path.elementAt(i-1);
+ QPointF delta = e - a;
+ *current = i;
+ return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
+ }
+ case QPainterPath::CurveToElement:
+ {
+ QBezier b = QBezier::fromPoints(path.elementAt(i-1),
+ e,
+ path.elementAt(i+1),
+ path.elementAt(i+2));
+ *bezLength = b.length();
+ *current = i;
+ return b;
+ }
+ default:
+ break;
+ }
+ }
+ *current = lastElement;
+ *bezLength = 0;
+ return QBezier();
+}
+
+//derivative of the equation
+static inline qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d)
+{
+ return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
+}
+
+void QDeclarativePath::createPointCache() const
+{
+ Q_D(const QDeclarativePath);
+ qreal pathLength = d->pathLength;
+ if (pathLength <= 0 || qIsNaN(pathLength))
+ return;
+ // more points means less jitter between items as they move along the
+ // path, but takes longer to generate
+ const int points = qCeil(pathLength*5);
+ const int lastElement = d->_path.elementCount() - 1;
+ d->_pointCache.resize(points+1);
+
+ int currElement = -1;
+ qreal bezLength = 0;
+ QBezier currBez = nextBezier(d->_path, &currElement, &bezLength);
+ qreal currLength = bezLength;
+ qreal epc = currLength / pathLength;
+
+ for (int i = 0; i < d->_pointCache.size(); i++) {
+ //find which set we are in
+ qreal prevPercent = 0;
+ qreal prevOrigPercent = 0;
+ for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
+ qreal percent = qreal(i)/points;
+ const AttributePoint &point = d->_attributePoints.at(ii);
+ if (percent < point.percent || ii == d->_attributePoints.count() - 1) { //### || is special case for very last item
+ qreal elementPercent = (percent - prevPercent);
+
+ qreal spc = prevOrigPercent + elementPercent * point.scale;
+
+ while (spc > epc) {
+ if (currElement > lastElement)
+ break;
+ currBez = nextBezier(d->_path, &currElement, &bezLength);
+ if (bezLength == 0.0) {
+ currLength = pathLength;
+ epc = 1.0;
+ break;
+ }
+ currLength += bezLength;
+ epc = currLength / pathLength;
+ }
+ qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
+ d->_pointCache[i] = currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
+ break;
+ }
+ prevOrigPercent = point.origpercent;
+ prevPercent = point.percent;
+ }
+ }
+}
+
+QPointF QDeclarativePath::sequentialPointAt(qreal p, qreal *angle) const
+{
+ Q_D(const QDeclarativePath);
+ return sequentialPointAt(d->_path, d->pathLength, d->_attributePoints, d->prevBez, p, angle);
+}
+
+QPointF QDeclarativePath::sequentialPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &prevBez, qreal p, qreal *angle)
+{
+ if (!prevBez.isValid)
+ return p > .5 ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
+ forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
+
+ return p < prevBez.p ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
+ forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
+}
+
+QPointF QDeclarativePath::forwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &prevBez, qreal p, qreal *angle)
+{
+ if (pathLength <= 0 || qIsNaN(pathLength))
+ return path.pointAtPercent(0); //expensive?
+
+ const int lastElement = path.elementCount() - 1;
+ bool haveCachedBez = prevBez.isValid;
+ int currElement = haveCachedBez ? prevBez.element : -1;
+ qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
+ QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength);
+ qreal currLength = haveCachedBez ? prevBez.currLength : bezLength;
+ qreal epc = currLength / pathLength;
+
+ //find which set we are in
+ qreal prevPercent = 0;
+ qreal prevOrigPercent = 0;
+ for (int ii = 0; ii < attributePoints.count(); ++ii) {
+ qreal percent = p;
+ const AttributePoint &point = attributePoints.at(ii);
+ if (percent < point.percent || ii == attributePoints.count() - 1) {
+ qreal elementPercent = (percent - prevPercent);
+
+ qreal spc = prevOrigPercent + elementPercent * point.scale;
+
+ while (spc > epc) {
+ Q_ASSERT(!(currElement > lastElement));
+ Q_UNUSED(lastElement);
+ currBez = nextBezier(path, &currElement, &bezLength);
+ currLength += bezLength;
+ epc = currLength / pathLength;
+ }
+ prevBez.element = currElement;
+ prevBez.bezLength = bezLength;
+ prevBez.currLength = currLength;
+ prevBez.bezier = currBez;
+ prevBez.p = p;
+ prevBez.isValid = true;
+
+ qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
+
+ if (angle) {
+ qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
+ qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
+ *angle = QLineF(0, 0, m1, m2).angle();
+ }
+
+ return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
+ }
+ prevOrigPercent = point.origpercent;
+ prevPercent = point.percent;
+ }
+
+ return QPointF(0,0);
+}
+
+//ideally this should be merged with forwardsPointAt
+QPointF QDeclarativePath::backwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &prevBez, qreal p, qreal *angle)
+{
+ if (pathLength <= 0 || qIsNaN(pathLength))
+ return path.pointAtPercent(0);
+
+ const int firstElement = 0;
+ bool haveCachedBez = prevBez.isValid;
+ int currElement = haveCachedBez ? prevBez.element : path.elementCount();
+ qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
+ QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength, true /*reverse*/);
+ qreal currLength = haveCachedBez ? prevBez.currLength : pathLength;
+ qreal prevLength = currLength - bezLength;
+ qreal epc = prevLength / pathLength;
+
+ for (int ii = attributePoints.count() - 1; ii > 0; --ii) {
+ qreal percent = p;
+ const AttributePoint &point = attributePoints.at(ii);
+ const AttributePoint &prevPoint = attributePoints.at(ii-1);
+ if (percent > prevPoint.percent || ii == 1) {
+ qreal elementPercent = (percent - prevPoint.percent);
+
+ qreal spc = prevPoint.origpercent + elementPercent * point.scale;
+
+ while (spc < epc) {
+ Q_ASSERT(!(currElement < firstElement));
+ Q_UNUSED(firstElement);
+ currBez = nextBezier(path, &currElement, &bezLength, true /*reverse*/);
+ currLength = prevLength;
+ prevLength = currLength - bezLength;
+ epc = prevLength / pathLength;
+ }
+ prevBez.element = currElement;
+ prevBez.bezLength = bezLength;
+ prevBez.currLength = currLength;
+ prevBez.bezier = currBez;
+ prevBez.p = p;
+ prevBez.isValid = true;
+
+ qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
+
+ if (angle) {
+ qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
+ qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
+ *angle = QLineF(0, 0, m1, m2).angle();
+ }
+
+ return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
+ }
+ }
+
+ return QPointF(0,0);
+}
+
+QPointF QDeclarativePath::pointAt(qreal p) const
+{
+ Q_D(const QDeclarativePath);
+ if (d->_pointCache.isEmpty()) {
+ createPointCache();
+ if (d->_pointCache.isEmpty())
+ return QPointF();
+ }
+ int idx = qRound(p*d->_pointCache.size());
+ if (idx >= d->_pointCache.size())
+ idx = d->_pointCache.size() - 1;
+ else if (idx < 0)
+ idx = 0;
+ return d->_pointCache.at(idx);
+}
+
+qreal QDeclarativePath::attributeAt(const QString &name, qreal percent) const
+{
+ Q_D(const QDeclarativePath);
+ if (percent < 0 || percent > 1)
+ return 0;
+
+ for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
+ const AttributePoint &point = d->_attributePoints.at(ii);
+
+ if (point.percent == percent) {
+ return point.values.value(name);
+ } else if (point.percent > percent) {
+ qreal lastValue =
+ ii?(d->_attributePoints.at(ii - 1).values.value(name)):0;
+ qreal lastPercent =
+ ii?(d->_attributePoints.at(ii - 1).percent):0;
+ qreal curValue = point.values.value(name);
+ qreal curPercent = point.percent;
+
+ return lastValue + (curValue - lastValue) * (percent - lastPercent) / (curPercent - lastPercent);
+ }
+ }
+
+ return 0;
+}
+
+/****************************************************************************/
+
+qreal QDeclarativeCurve::x() const
+{
+ return _x.isNull ? 0 : _x.value;
+}
+
+void QDeclarativeCurve::setX(qreal x)
+{
+ if (_x.isNull || _x != x) {
+ _x = x;
+ emit xChanged();
+ emit changed();
+ }
+}
+
+bool QDeclarativeCurve::hasX()
+{
+ return _x.isValid();
+}
+
+qreal QDeclarativeCurve::y() const
+{
+ return _y.isNull ? 0 : _y.value;
+}
+
+void QDeclarativeCurve::setY(qreal y)
+{
+ if (_y.isNull || _y != y) {
+ _y = y;
+ emit yChanged();
+ emit changed();
+ }
+}
+
+bool QDeclarativeCurve::hasY()
+{
+ return _y.isValid();
+}
+
+qreal QDeclarativeCurve::relativeX() const
+{
+ return _relativeX;
+}
+
+void QDeclarativeCurve::setRelativeX(qreal x)
+{
+ if (_relativeX.isNull || _relativeX != x) {
+ _relativeX = x;
+ emit relativeXChanged();
+ emit changed();
+ }
+}
+
+bool QDeclarativeCurve::hasRelativeX()
+{
+ return _relativeX.isValid();
+}
+
+qreal QDeclarativeCurve::relativeY() const
+{
+ return _relativeY;
+}
+
+void QDeclarativeCurve::setRelativeY(qreal y)
+{
+ if (_relativeY.isNull || _relativeY != y) {
+ _relativeY = y;
+ emit relativeYChanged();
+ emit changed();
+ }
+}
+
+bool QDeclarativeCurve::hasRelativeY()
+{
+ return _relativeY.isValid();
+}
+
+/****************************************************************************/
+
+/*!
+ \qmlclass PathAttribute QDeclarativePathAttribute
+ \inqmlmodule QtQuick 2
+ \ingroup qml-view-elements
+ \brief The PathAttribute allows setting an attribute at a given position in a Path.
+
+ The PathAttribute object allows attributes consisting of a name and
+ a value to be specified for various points along a path. The
+ attributes are exposed to the delegate as
+ \l{qdeclarativeintroduction.html#attached-properties} {Attached Properties}.
+ The value of an attribute at any particular point along the path is interpolated
+ from the PathAttributes bounding that point.
+
+ The example below shows a path with the items scaled to 30% with
+ opacity 50% at the top of the path and scaled 100% with opacity
+ 100% at the bottom. Note the use of the PathView.iconScale and
+ PathView.iconOpacity attached properties to set the scale and opacity
+ of the delegate.
+
+ \table
+ \row
+ \o \image declarative-pathattribute.png
+ \o
+ \snippet doc/src/snippets/declarative/pathview/pathattributes.qml 0
+ (see the PathView documentation for the specification of ContactModel.qml
+ used for ContactModel above.)
+ \endtable
+
+
+ \sa Path
+*/
+
+/*!
+ \qmlproperty string QtQuick2::PathAttribute::name
+ This property holds the name of the attribute to change.
+
+ This attribute will be available to the delegate as PathView.<name>
+
+ Note that using an existing Item property name such as "opacity" as an
+ attribute is allowed. This is because path attributes add a new
+ \l{qdeclarativeintroduction.html#attached-properties} {Attached Property}
+ which in no way clashes with existing properties.
+*/
+
+/*!
+ the name of the attribute to change.
+*/
+
+QString QDeclarativePathAttribute::name() const
+{
+ return _name;
+}
+
+void QDeclarativePathAttribute::setName(const QString &name)
+{
+ if (_name == name)
+ return;
+ _name = name;
+ emit nameChanged();
+}
+
+/*!
+ \qmlproperty real QtQuick2::PathAttribute::value
+ This property holds the value for the attribute.
+
+ The value specified can be used to influence the visual appearance
+ of an item along the path. For example, the following Path specifies
+ an attribute named \e itemRotation, which has the value \e 0 at the
+ beginning of the path, and the value 90 at the end of the path.
+
+ \qml
+ Path {
+ startX: 0
+ startY: 0
+ PathAttribute { name: "itemRotation"; value: 0 }
+ PathLine { x: 100; y: 100 }
+ PathAttribute { name: "itemRotation"; value: 90 }
+ }
+ \endqml
+
+ In our delegate, we can then bind the \e rotation property to the
+ \l{qdeclarativeintroduction.html#attached-properties} {Attached Property}
+ \e PathView.itemRotation created for this attribute.
+
+ \qml
+ Rectangle {
+ width: 10; height: 10
+ rotation: PathView.itemRotation
+ }
+ \endqml
+
+ As each item is positioned along the path, it will be rotated accordingly:
+ an item at the beginning of the path with be not be rotated, an item at
+ the end of the path will be rotated 90 degrees, and an item mid-way along
+ the path will be rotated 45 degrees.
+*/
+
+/*!
+ the new value of the attribute.
+*/
+qreal QDeclarativePathAttribute::value() const
+{
+ return _value;
+}
+
+void QDeclarativePathAttribute::setValue(qreal value)
+{
+ if (_value != value) {
+ _value = value;
+ emit valueChanged();
+ emit changed();
+ }
+}
+
+/****************************************************************************/
+
+/*!
+ \qmlclass PathLine QDeclarativePathLine
+ \inqmlmodule QtQuick 2
+ \ingroup qml-view-elements
+ \brief The PathLine defines a straight line.
+
+ The example below creates a path consisting of a straight line from
+ 0,100 to 200,100:
+
+ \qml
+ Path {
+ startX: 0; startY: 100
+ PathLine { x: 200; y: 100 }
+ }
+ \endqml
+
+ \sa Path, PathQuad, PathCubic
+*/
+
+/*!
+ \qmlproperty real QtQuick2::PathLine::x
+ \qmlproperty real QtQuick2::PathLine::y
+
+ Defines the end point of the line.
+*/
+
+inline QPointF positionForCurve(const QDeclarativePathData &data, const QPointF &prevPoint)
+{
+ QDeclarativeCurve *curve = data.curves.at(data.index);
+ bool isEnd = data.index == data.curves.size() - 1;
+ return QPointF(curve->hasRelativeX() ? prevPoint.x() + curve->relativeX() : !isEnd || curve->hasX() ? curve->x() : data.endPoint.x(),
+ curve->hasRelativeY() ? prevPoint.y() + curve->relativeY() : !isEnd || curve->hasY() ? curve->y() : data.endPoint.y());
+}
+
+void QDeclarativePathLine::addToPath(QPainterPath &path, const QDeclarativePathData &data)
+{
+ path.lineTo(positionForCurve(data, path.currentPosition()));
+}
+
+/****************************************************************************/
+
+/*!
+ \qmlclass PathQuad QDeclarativePathQuad
+ \inqmlmodule QtQuick 2
+ \ingroup qml-view-elements
+ \brief The PathQuad defines a quadratic Bezier curve with a control point.
+
+ The following QML produces the path shown below:
+ \table
+ \row
+ \o \image declarative-pathquad.png
+ \o
+ \qml
+ Path {
+ startX: 0; startY: 0
+ PathQuad { x: 200; y: 0; controlX: 100; controlY: 150 }
+ }
+ \endqml
+ \endtable
+
+ \sa Path, PathCubic, PathLine
+*/
+
+/*!
+ \qmlproperty real QtQuick2::PathQuad::x
+ \qmlproperty real QtQuick2::PathQuad::y
+
+ Defines the end point of the curve.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::PathQuad::controlX
+ \qmlproperty real QtQuick2::PathQuad::controlY
+
+ Defines the position of the control point.
+*/
+
+/*!
+ the x position of the control point.
+*/
+qreal QDeclarativePathQuad::controlX() const
+{
+ return _controlX;
+}
+
+void QDeclarativePathQuad::setControlX(qreal x)
+{
+ if (_controlX != x) {
+ _controlX = x;
+ emit controlXChanged();
+ emit changed();
+ }
+}
+
+
+/*!
+ the y position of the control point.
+*/
+qreal QDeclarativePathQuad::controlY() const
+{
+ return _controlY;
+}
+
+void QDeclarativePathQuad::setControlY(qreal y)
+{
+ if (_controlY != y) {
+ _controlY = y;
+ emit controlYChanged();
+ emit changed();
+ }
+}
+
+qreal QDeclarativePathQuad::relativeControlX() const
+{
+ return _relativeControlX;
+}
+
+void QDeclarativePathQuad::setRelativeControlX(qreal x)
+{
+ if (_relativeControlX.isNull || _relativeControlX != x) {
+ _relativeControlX = x;
+ emit relativeControlXChanged();
+ emit changed();
+ }
+}
+
+bool QDeclarativePathQuad::hasRelativeControlX()
+{
+ return _relativeControlX.isValid();
+}
+
+qreal QDeclarativePathQuad::relativeControlY() const
+{
+ return _relativeControlY;
+}
+
+void QDeclarativePathQuad::setRelativeControlY(qreal y)
+{
+ if (_relativeControlY.isNull || _relativeControlY != y) {
+ _relativeControlY = y;
+ emit relativeControlYChanged();
+ emit changed();
+ }
+}
+
+bool QDeclarativePathQuad::hasRelativeControlY()
+{
+ return _relativeControlY.isValid();
+}
+
+void QDeclarativePathQuad::addToPath(QPainterPath &path, const QDeclarativePathData &data)
+{
+ const QPointF &prevPoint = path.currentPosition();
+ QPointF controlPoint(hasRelativeControlX() ? prevPoint.x() + relativeControlX() : controlX(),
+ hasRelativeControlY() ? prevPoint.y() + relativeControlY() : controlY());
+ path.quadTo(controlPoint, positionForCurve(data, path.currentPosition()));
+}
+
+/****************************************************************************/
+
+/*!
+ \qmlclass PathCubic QDeclarativePathCubic
+ \inqmlmodule QtQuick 2
+ \ingroup qml-view-elements
+ \brief The PathCubic defines a cubic Bezier curve with two control points.
+
+ The following QML produces the path shown below:
+ \table
+ \row
+ \o \image declarative-pathcubic.png
+ \o
+ \qml
+ Path {
+ startX: 20; startY: 0
+ PathCubic {
+ x: 180; y: 0
+ control1X: -10; control1Y: 90
+ control2X: 210; control2Y: 90
+ }
+ }
+ \endqml
+ \endtable
+
+ \sa Path, PathQuad, PathLine
+*/
+
+/*!
+ \qmlproperty real QtQuick2::PathCubic::x
+ \qmlproperty real QtQuick2::PathCubic::y
+
+ Defines the end point of the curve.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::PathCubic::control1X
+ \qmlproperty real QtQuick2::PathCubic::control1Y
+
+ Defines the position of the first control point.
+*/
+qreal QDeclarativePathCubic::control1X() const
+{
+ return _control1X;
+}
+
+void QDeclarativePathCubic::setControl1X(qreal x)
+{
+ if (_control1X != x) {
+ _control1X = x;
+ emit control1XChanged();
+ emit changed();
+ }
+}
+
+qreal QDeclarativePathCubic::control1Y() const
+{
+ return _control1Y;
+}
+
+void QDeclarativePathCubic::setControl1Y(qreal y)
+{
+ if (_control1Y != y) {
+ _control1Y = y;
+ emit control1YChanged();
+ emit changed();
+ }
+}
+
+/*!
+ \qmlproperty real QtQuick2::PathCubic::control2X
+ \qmlproperty real QtQuick2::PathCubic::control2Y
+
+ Defines the position of the second control point.
+*/
+qreal QDeclarativePathCubic::control2X() const
+{
+ return _control2X;
+}
+
+void QDeclarativePathCubic::setControl2X(qreal x)
+{
+ if (_control2X != x) {
+ _control2X = x;
+ emit control2XChanged();
+ emit changed();
+ }
+}
+
+qreal QDeclarativePathCubic::control2Y() const
+{
+ return _control2Y;
+}
+
+void QDeclarativePathCubic::setControl2Y(qreal y)
+{
+ if (_control2Y != y) {
+ _control2Y = y;
+ emit control2YChanged();
+ emit changed();
+ }
+}
+
+qreal QDeclarativePathCubic::relativeControl1X() const
+{
+ return _relativeControl1X;
+}
+
+void QDeclarativePathCubic::setRelativeControl1X(qreal x)
+{
+ if (_relativeControl1X.isNull || _relativeControl1X != x) {
+ _relativeControl1X = x;
+ emit relativeControl1XChanged();
+ emit changed();
+ }
+}
+
+bool QDeclarativePathCubic::hasRelativeControl1X()
+{
+ return _relativeControl1X.isValid();
+}
+
+qreal QDeclarativePathCubic::relativeControl1Y() const
+{
+ return _relativeControl1Y;
+}
+
+void QDeclarativePathCubic::setRelativeControl1Y(qreal y)
+{
+ if (_relativeControl1Y.isNull || _relativeControl1Y != y) {
+ _relativeControl1Y = y;
+ emit relativeControl1YChanged();
+ emit changed();
+ }
+}
+
+bool QDeclarativePathCubic::hasRelativeControl1Y()
+{
+ return _relativeControl1Y.isValid();
+}
+
+qreal QDeclarativePathCubic::relativeControl2X() const
+{
+ return _relativeControl2X;
+}
+
+void QDeclarativePathCubic::setRelativeControl2X(qreal x)
+{
+ if (_relativeControl2X.isNull || _relativeControl2X != x) {
+ _relativeControl2X = x;
+ emit relativeControl2XChanged();
+ emit changed();
+ }
+}
+
+bool QDeclarativePathCubic::hasRelativeControl2X()
+{
+ return _relativeControl2X.isValid();
+}
+
+qreal QDeclarativePathCubic::relativeControl2Y() const
+{
+ return _relativeControl2Y;
+}
+
+void QDeclarativePathCubic::setRelativeControl2Y(qreal y)
+{
+ if (_relativeControl2Y.isNull || _relativeControl2Y != y) {
+ _relativeControl2Y = y;
+ emit relativeControl2YChanged();
+ emit changed();
+ }
+}
+
+bool QDeclarativePathCubic::hasRelativeControl2Y()
+{
+ return _relativeControl2Y.isValid();
+}
+
+void QDeclarativePathCubic::addToPath(QPainterPath &path, const QDeclarativePathData &data)
+{
+ const QPointF &prevPoint = path.currentPosition();
+ QPointF controlPoint1(hasRelativeControl1X() ? prevPoint.x() + relativeControl1X() : control1X(),
+ hasRelativeControl1Y() ? prevPoint.y() + relativeControl1Y() : control1Y());
+ QPointF controlPoint2(hasRelativeControl2X() ? prevPoint.x() + relativeControl2X() : control2X(),
+ hasRelativeControl2Y() ? prevPoint.y() + relativeControl2Y() : control2Y());
+ path.cubicTo(controlPoint1, controlPoint2, positionForCurve(data, path.currentPosition()));
+}
+
+/****************************************************************************/
+
+inline QPointF previousPathPosition(const QPainterPath &path)
+{
+ int count = path.elementCount();
+ if (count < 1)
+ return QPointF();
+
+ int index = path.elementAt(count-1).type == QPainterPath::CurveToDataElement ? count - 4 : count - 2;
+ return index > -1 ? QPointF(path.elementAt(index)) : path.pointAtPercent(0);
+}
+
+void QDeclarativePathCatmullRomCurve::addToPath(QPainterPath &path, const QDeclarativePathData &data)
+{
+ //here we convert catmull-rom spline to bezier for use in QPainterPath.
+ //basic conversion algorithm:
+ // catmull-rom points * inverse bezier matrix * catmull-rom matrix = bezier points
+ //each point in the catmull-rom spline produces a bezier endpoint + 2 control points
+ //calculations for each point use a moving window of 4 points
+ // (previous 2 points + current point + next point)
+ QPointF prevFar, prev, point, next;
+
+ //get previous points
+ int index = data.index - 1;
+ QDeclarativeCurve *curve = index == -1 ? 0 : data.curves.at(index);
+ if (qobject_cast<QDeclarativePathCatmullRomCurve*>(curve)) {
+ prev = path.currentPosition();
+ prevFar = previousPathPosition(path);
+ } else
+ prevFar = prev = path.currentPosition();
+
+ //get current point
+ point = positionForCurve(data, path.currentPosition());
+
+ //get next point
+ index = data.index + 1;
+ if (index < data.curves.count() && qobject_cast<QDeclarativePathCatmullRomCurve*>(data.curves.at(index))) {
+ QDeclarativePathData nextData;
+ nextData.index = index;
+ nextData.endPoint = data.endPoint;
+ nextData.curves = data.curves;
+ next = positionForCurve(nextData, point);
+ } else
+ next = point;
+
+ /*
+ full conversion matrix (inverse bezier * catmull-rom):
+ 0.000, 1.000, 0.000, 0.000,
+ -0.167, 1.000, 0.167, 0.000,
+ 0.000, 0.167, 1.000, -0.167,
+ 0.000, 0.000, 1.000, 0.000
+
+ conversion doesn't require full matrix multiplication,
+ so below we simplify
+ */
+ QPointF control1(prevFar.x() * qreal(-0.167) +
+ prev.x() +
+ point.x() * qreal(0.167),
+ prevFar.y() * qreal(-0.167) +
+ prev.y() +
+ point.y() * qreal(0.167));
+
+ QPointF control2(prev.x() * qreal(0.167) +
+ point.x() +
+ next.x() * qreal(-0.167),
+ prev.y() * qreal(0.167) +
+ point.y() +
+ next.y() * qreal(-0.167));
+
+ path.cubicTo(control1, control2, point);
+}
+
+/****************************************************************************/
+
+qreal QDeclarativePathArc::radiusX() const
+{
+ return _radiusX;
+}
+
+void QDeclarativePathArc::setRadiusX(qreal radius)
+{
+ if (_radiusX == radius)
+ return;
+
+ _radiusX = radius;
+ emit radiusXChanged();
+}
+
+qreal QDeclarativePathArc::radiusY() const
+{
+ return _radiusY;
+}
+
+void QDeclarativePathArc::setRadiusY(qreal radius)
+{
+ if (_radiusY == radius)
+ return;
+
+ _radiusY = radius;
+ emit radiusYChanged();
+}
+
+bool QDeclarativePathArc::useLargeArc() const
+{
+ return _useLargeArc;
+}
+
+void QDeclarativePathArc::setUseLargeArc(bool largeArc)
+{
+ if (_useLargeArc == largeArc)
+ return;
+
+ _useLargeArc = largeArc;
+ emit useLargeArcChanged();
+}
+
+QDeclarativePathArc::ArcDirection QDeclarativePathArc::direction() const
+{
+ return _direction;
+}
+
+void QDeclarativePathArc::setDirection(ArcDirection direction)
+{
+ if (_direction == direction)
+ return;
+
+ _direction = direction;
+ emit directionChanged();
+}
+
+void QDeclarativePathArc::addToPath(QPainterPath &path, const QDeclarativePathData &data)
+{
+ const QPointF &startPoint = path.currentPosition();
+ const QPointF &endPoint = positionForCurve(data, startPoint);
+ QDeclarativeSvgParser::pathArc(path,
+ _radiusX,
+ _radiusY,
+ 0, //xAxisRotation
+ _useLargeArc,
+ _direction == Clockwise ? 1 : 0,
+ endPoint.x(),
+ endPoint.y(),
+ startPoint.x(), startPoint.y());
+}
+
+/****************************************************************************/
+
+QString QDeclarativePathSvg::path() const
+{
+ return _path;
+}
+
+void QDeclarativePathSvg::setPath(const QString &path)
+{
+ if (_path == path)
+ return;
+
+ _path = path;
+ emit pathChanged();
+}
+
+void QDeclarativePathSvg::addToPath(QPainterPath &path, const QDeclarativePathData &)
+{
+ QDeclarativeSvgParser::parsePathDataFast(_path, path);
+}
+
+/****************************************************************************/
+
+/*!
+ \qmlclass PathPercent QDeclarativePathPercent
+ \inqmlmodule QtQuick 2
+ \ingroup qml-view-elements
+ \brief The PathPercent manipulates the way a path is interpreted.
+
+ PathPercent allows you to manipulate the spacing between items on a
+ PathView's path. You can use it to bunch together items on part of
+ the path, and spread them out on other parts of the path.
+
+ The examples below show the normal distribution of items along a path
+ compared to a distribution which places 50% of the items along the
+ PathLine section of the path.
+ \table
+ \row
+ \o \image declarative-nopercent.png
+ \o
+ \qml
+ PathView {
+ // ...
+ Path {
+ startX: 20; startY: 0
+ PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
+ PathLine { x: 150; y: 80 }
+ PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
+ }
+ }
+ \endqml
+ \row
+ \o \image declarative-percent.png
+ \o
+ \qml
+ PathView {
+ // ...
+ Path {
+ startX: 20; startY: 0
+ PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
+ PathPercent { value: 0.25 }
+ PathLine { x: 150; y: 80 }
+ PathPercent { value: 0.75 }
+ PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
+ PathPercent { value: 1 }
+ }
+ }
+ \endqml
+ \endtable
+
+ \sa Path
+*/
+
+/*!
+ \qmlproperty real QtQuick2::PathPercent::value
+ The proportion of items that should be laid out up to this point.
+
+ This value should always be higher than the last value specified
+ by a PathPercent at a previous position in the Path.
+
+ In the following example we have a Path made up of three PathLines.
+ Normally, the items of the PathView would be laid out equally along
+ this path, with an equal number of items per line segment. PathPercent
+ allows us to specify that the first and third lines should each hold
+ 10% of the laid out items, while the second line should hold the remaining
+ 80%.
+
+ \qml
+ PathView {
+ // ...
+ Path {
+ startX: 0; startY: 0
+ PathLine { x:100; y: 0; }
+ PathPercent { value: 0.1 }
+ PathLine { x: 100; y: 100 }
+ PathPercent { value: 0.9 }
+ PathLine { x: 100; y: 0 }
+ PathPercent { value: 1 }
+ }
+ }
+ \endqml
+*/
+
+qreal QDeclarativePathPercent::value() const
+{
+ return _value;
+}
+
+void QDeclarativePathPercent::setValue(qreal value)
+{
+ if (_value != value) {
+ _value = value;
+ emit valueChanged();
+ emit changed();
+ }
+}
+QT_END_NAMESPACE