summaryrefslogtreecommitdiffstats
path: root/src/declarative/fx/qfxpath.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/declarative/fx/qfxpath.cpp')
-rw-r--r--src/declarative/fx/qfxpath.cpp745
1 files changed, 745 insertions, 0 deletions
diff --git a/src/declarative/fx/qfxpath.cpp b/src/declarative/fx/qfxpath.cpp
new file mode 100644
index 0000000..199f5fb
--- /dev/null
+++ b/src/declarative/fx/qfxpath.cpp
@@ -0,0 +1,745 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.0, 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.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfxpath.h"
+#include "qfxpath_p.h"
+#include <qfxperf.h>
+#include <private/qbezier_p.h>
+
+
+QT_BEGIN_NAMESPACE
+QML_DEFINE_TYPE(QFxPath,Path);
+QML_DEFINE_TYPE(QFxPathElement,PathElement);
+QML_DEFINE_TYPE(QFxCurve,Curve);
+QML_DEFINE_TYPE(QFxPathAttribute,PathAttribute);
+QML_DEFINE_TYPE(QFxPathPercent,PathPercent);
+QML_DEFINE_TYPE(QFxPathLine,PathLine);
+QML_DEFINE_TYPE(QFxPathQuad,PathQuad);
+QML_DEFINE_TYPE(QFxPathCubic,PathCubic);
+
+/*!
+ \qmlclass Path
+ \brief The Path element defines a path for use by \l PathView.
+
+ \sa PathElement, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic
+*/
+
+/*!
+ \internal
+ \class QFxPath
+ \ingroup utility
+ \brief The QFxPath class defines a path.
+ \sa QFxPathView
+*/
+QFxPath::QFxPath(QObject *parent)
+ : QObject(*(new QFxPathPrivate), parent)
+{
+}
+
+QFxPath::QFxPath(QFxPathPrivate &dd, QObject *parent)
+ : QObject(dd, parent)
+{
+}
+
+QFxPath::~QFxPath()
+{
+}
+
+/*!
+ \qmlproperty int Path::startX
+ This property holds the starting x position of the path.
+*/
+
+/*!
+ \property QFxPath::startX
+ \brief the starting x position of the path.
+*/
+
+int QFxPath::startX() const
+{
+ Q_D(const QFxPath);
+ return d->startX;
+}
+
+void QFxPath::setStartX(int x)
+{
+ Q_D(QFxPath);
+ d->startX = x;
+}
+
+/*!
+ \qmlproperty int Path::startY
+ This property holds the starting y position of the path.
+*/
+
+/*!
+ \property QFxPath::startY
+ \brief the starting y position of the path.
+*/
+
+int QFxPath::startY() const
+{
+ Q_D(const QFxPath);
+ return d->startY;
+}
+
+void QFxPath::setStartY(int y)
+{
+ Q_D(QFxPath);
+ d->startY = y;
+}
+
+/*!
+ \qmlproperty list<PathElement> Path::pathElements
+ This property holds the elements composing the path.
+
+ \default
+
+ A path can contain the following path elements:
+ \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
+
+ \code
+ <Path startX="240" startY="350">
+ <PathAttribute name="scale" value="1.0"/>
+ <PathAttribute name="opacity" value="1"/>
+ <PathQuad x="240" y="150" controlX="660" controlY="250"/>
+ <PathAttribute name="scale" value="0.1"/>
+ <PathAttribute name="opacity" value="-0.5"/>
+ <PathCubic x="240" y="350" control1X="-180" control1Y="250" control2X="0" control2Y="25"/>
+ </Path>
+ \endcode
+*/
+
+QList<QFxPathElement *>* QFxPath::pathElements()
+{
+ Q_D(QFxPath);
+ return &(d->_pathElements);
+}
+
+void QFxPath::interpolate(int idx, const QString &name, qreal value)
+{
+ Q_D(QFxPath);
+ if(!idx)
+ return;
+
+ qreal lastValue = 0;
+ qreal lastPercent = 0;
+ int search = idx - 1;
+ while(search >= 0) {
+ const AttributePoint &point = d->_attributePoints.at(search);
+ if(point.values.contains(name)) {
+ lastValue = point.values.value(name);
+ lastPercent = point.origpercent;
+ break;
+ }
+ --search;
+ }
+
+ ++search;
+
+ const AttributePoint &curPoint = d->_attributePoints.at(idx);
+
+ for(int ii = search; ii < idx; ++ii) {
+ AttributePoint &point = d->_attributePoints[ii];
+
+ qreal val = lastValue + (value - lastValue) * (point.origpercent - lastPercent) / (curPoint.origpercent - lastPercent);
+ point.values.insert(name, val);
+ }
+}
+
+void QFxPath::endpoint(const QString &name)
+{
+ Q_D(QFxPath);
+ 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 QFxPath::processPath()
+{
+ Q_D(QFxPath);
+
+ d->_pointCache.clear();
+ d->_attributePoints.clear();
+ d->_path = QPainterPath();
+
+ AttributePoint first;
+ for(int ii = 0; ii < d->_attributes.count(); ++ii)
+ first.values[d->_attributes.at(ii)] = 0;
+ d->_attributePoints << first;
+
+ d->_path.moveTo(d->startX, d->startY);
+
+ foreach (QFxPathElement *pathElement, d->_pathElements) {
+ if(QFxCurve *curve = qobject_cast<QFxCurve *>(pathElement)) {
+ curve->addToPath(d->_path);
+ AttributePoint p;
+ p.origpercent = d->_path.length();
+ d->_attributePoints << p;
+ } else if(QFxPathAttribute *attribute = qobject_cast<QFxPathAttribute *>(pathElement)) {
+ AttributePoint &point = d->_attributePoints.last();
+ point.values[attribute->name()] = attribute->value();
+ interpolate(d->_attributePoints.count() - 1, attribute->name(), attribute->value());
+ } else if(QFxPathPercent *percent = qobject_cast<QFxPathPercent *>(pathElement)) {
+ AttributePoint &point = d->_attributePoints.last();
+ point.values[QLatin1String("_qfx_percent")] = percent->value();
+ interpolate(d->_attributePoints.count() - 1, QLatin1String("_qfx_percent"), percent->value());
+ }
+ }
+
+ // Fixup end points
+ const AttributePoint &last = d->_attributePoints.last();
+ for(int ii = 0; ii < d->_attributes.count(); ++ii) {
+ if(!last.values.contains(d->_attributes.at(ii)))
+ endpoint(d->_attributes.at(ii));
+ }
+
+ // Adjust percent
+ qreal length = d->_path.length();
+ qreal prevpercent = 0;
+ qreal prevorigpercent = 0;
+ for(int ii = 0; ii < d->_attributePoints.count(); ++ii) {
+ const AttributePoint &point = d->_attributePoints.at(ii);
+ if(point.values.contains(QLatin1String("_qfx_percent"))) { //special string for QFxPathPercent
+ if ( ii > 0) {
+ qreal scale = (d->_attributePoints[ii].origpercent/length - prevorigpercent) /
+ (point.values.value(QLatin1String("_qfx_percent"))-prevpercent);
+ d->_attributePoints[ii].scale = scale;
+ }
+ d->_attributePoints[ii].origpercent /= length;
+ d->_attributePoints[ii].percent = point.values.value(QLatin1String("_qfx_percent"));
+ prevorigpercent = d->_attributePoints[ii].origpercent;
+ prevpercent = d->_attributePoints[ii].percent;
+ } else {
+ d->_attributePoints[ii].origpercent /= length;
+ d->_attributePoints[ii].percent = d->_attributePoints[ii].origpercent;
+ }
+ }
+
+ emit changed();
+}
+
+void QFxPath::componentComplete()
+{
+ Q_D(QFxPath);
+ QSet<QString> attrs;
+ // First gather up all the attributes
+ foreach (QFxPathElement *pathElement, d->_pathElements) {
+ if(QFxPathAttribute *attribute =
+ qobject_cast<QFxPathAttribute *>(pathElement))
+ attrs.insert(attribute->name());
+ }
+ d->_attributes = attrs.toList();
+
+ processPath();
+
+ foreach (QFxPathElement *pathElement, d->_pathElements)
+ connect(pathElement, SIGNAL(changed()), this, SLOT(processPath()));
+}
+
+QPainterPath QFxPath::path() const
+{
+ Q_D(const QFxPath);
+ return d->_path;
+}
+
+QStringList QFxPath::attributes() const
+{
+ Q_D(const QFxPath);
+ return d->_attributes;
+}
+#include <QTime>
+
+static inline QBezier nextBezier(const QPainterPath &path, int *from, qreal *bezLength)
+{
+ const int lastElement = path.elementCount() - 1;
+ for (int i=*from; i <= lastElement; ++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;
+ *from = i+1;
+ 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();
+ *from = i+3;
+ return b;
+ }
+ default:
+ break;
+ }
+ }
+ *from = lastElement;
+ *bezLength = 0;
+ return QBezier();
+}
+
+void QFxPath::createPointCache() const
+{
+ Q_D(const QFxPath);
+#ifdef Q_ENABLE_PERFORMANCE_LOG
+ QFxPerfTimer<QFxPerf::PathCache> pc;
+#endif
+ qreal pathLength = d->_path.length();
+ const int points = int(pathLength*2);
+ const int lastElement = d->_path.elementCount() - 1;
+ d->_pointCache.resize(points+1);
+
+ int currElement = 0;
+ 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 QFxPath::pointAt(qreal p) const
+{
+ Q_D(const QFxPath);
+ if (d->_pointCache.isEmpty()) {
+ createPointCache();
+ }
+ 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 QFxPath::attributeAt(const QString &name, qreal percent) const
+{
+ Q_D(const QFxPath);
+ 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;
+}
+
+/****************************************************************************/
+
+int QFxCurve::x() const
+{
+ return _x;
+}
+
+void QFxCurve::setX(int x)
+{
+ if (_x != x) {
+ _x = x;
+ emit changed();
+ }
+}
+
+int QFxCurve::y() const
+{
+ return _y;
+}
+
+void QFxCurve::setY(int y)
+{
+ if (_y != y) {
+ _y = y;
+ emit changed();
+ }
+}
+
+/****************************************************************************/
+
+/*!
+ \qmlclass PathAttribute
+ \brief The PathAttribute allows to set an attribute at a given position in the path.
+*/
+
+/*!
+ \internal
+ \class QFxPathAttribute
+ \ingroup utility
+ \brief The QFxPathAttribute class allows to set the value of an attribute at a given position in the path.
+
+ \sa QFxPath
+*/
+
+
+/*!
+ \qmlproperty string PathAttribute::name
+ the name of the attribute to change.
+*/
+
+/*!
+ the name of the attribute to change.
+*/
+
+QString QFxPathAttribute::name() const
+{
+ return _name;
+}
+
+void QFxPathAttribute::setName(const QString &name)
+{
+ _name = name;
+}
+
+/*!
+ \qmlproperty string PathAttribute::value
+ the new value of the attribute.
+*/
+
+/*!
+ the new value of the attribute.
+*/
+qreal QFxPathAttribute::value() const
+{
+ return _value;
+}
+
+void QFxPathAttribute::setValue(qreal value)
+{
+ if (_value != value) {
+ _value = value;
+ emit changed();
+ }
+}
+
+/****************************************************************************/
+
+/*!
+ \qmlclass PathLine
+ \brief The PathLine defines a straight line.
+
+*/
+
+/*!
+ \internal
+ \class QFxPathLine
+ \ingroup utility
+ \brief The QFxPathLine class defines a straight line.
+
+ \sa QFxPath
+*/
+
+void QFxPathLine::addToPath(QPainterPath &path)
+{
+ path.lineTo(x(), y());
+}
+
+/****************************************************************************/
+
+/*!
+ \qmlclass PathQuad
+ \brief The PathQuad defines a quadratic Bezier curve with a control point.
+
+*/
+
+/*!
+ \internal
+ \class QFxPathQuad
+ \ingroup utility
+ \brief The QFxPathQuad class defines a quadratic Bezier curve with a control point.
+
+ \sa QFxPath
+*/
+
+/*!
+ \qmlproperty string PathQuad::controlX
+ the x position of the control point.
+*/
+
+/*!
+ the x position of the control point.
+*/
+int QFxPathQuad::controlX() const
+{
+ return _controlX;
+}
+
+void QFxPathQuad::setControlX(int x)
+{
+ if (_controlX != x) {
+ _controlX = x;
+ emit changed();
+ }
+}
+
+/*!
+ \qmlproperty string PathQuad::controlY
+ the y position of the control point.
+*/
+
+/*!
+ the y position of the control point.
+*/
+int QFxPathQuad::controlY() const
+{
+ return _controlY;
+}
+
+void QFxPathQuad::setControlY(int y)
+{
+ if (_controlY != y) {
+ _controlY = y;
+ emit changed();
+ }
+}
+
+void QFxPathQuad::addToPath(QPainterPath &path)
+{
+ path.quadTo(controlX(), controlY(), x(), y());
+}
+
+/****************************************************************************/
+
+/*!
+ \qmlclass PathCubic
+ \brief The PathCubic defines a cubic Bezier curve with two control points.
+
+*/
+
+/*!
+ \internal
+ \class QFxPathCubic
+ \ingroup utility
+ \brief The QFxPathCubic class defines a cubic Bezier curve with two control points.
+
+ \sa QFxPath
+*/
+
+/*!
+ \qmlproperty string PathCubic::control1X
+ the x position of the first control point.
+*/
+
+/*!
+ \property QFxPathCubic::control1X
+ \brief the x position of the first control point.
+*/
+int QFxPathCubic::control1X() const
+{
+ return _control1X;
+}
+
+void QFxPathCubic::setControl1X(int x)
+{
+ if (_control1X != x) {
+ _control1X = x;
+ emit changed();
+ }
+}
+
+/*!
+ \qmlproperty string PathCubic::control1Y
+ the y position of the first control point.
+*/
+
+/*!
+ \property QFxPathCubic::control1Y
+ \brief the y position of the first control point.
+*/
+int QFxPathCubic::control1Y() const
+{
+ return _control1Y;
+}
+
+void QFxPathCubic::setControl1Y(int y)
+{
+ if (_control1Y != y) {
+ _control1Y = y;
+ emit changed();
+ }
+}
+
+/*!
+ \qmlproperty string PathCubic::control2X
+ the x position of the second control point.
+*/
+
+/*!
+ \property QFxPathCubic::control2X
+ \brief the x position of the second control point.
+*/
+int QFxPathCubic::control2X() const
+{
+ return _control2X;
+}
+
+void QFxPathCubic::setControl2X(int x)
+{
+ if (_control2X != x) {
+ _control2X = x;
+ emit changed();
+ }
+}
+
+/*!
+ \qmlproperty string PathCubic::control2Y
+ the y position of the second control point.
+*/
+
+/*!
+ \property QFxPathCubic::control2Y
+ \brief the y position of the second control point.
+*/
+int QFxPathCubic::control2Y() const
+{
+ return _control2Y;
+}
+
+void QFxPathCubic::setControl2Y(int y)
+{
+ if (_control2Y != y) {
+ _control2Y = y;
+ emit changed();
+ }
+}
+
+void QFxPathCubic::addToPath(QPainterPath &path)
+{
+ path.cubicTo(control1X(), control1Y(), control2X(), control2Y(), x(), y());
+}
+
+/****************************************************************************/
+
+/*!
+ \qmlclass PathPercent
+ \brief The PathPercent manipulates the way a path is interpreted.
+
+*/
+
+/*!
+ \internal
+ \class QFxPathPercent
+ \ingroup utility
+ \brief The QFxPathPercent class manipulates the way a path is interpreted.
+
+ QFxPathPercent allows you to bunch up items (or spread out items) along various
+ segments of a QFxPathView's path.
+
+ \sa QFxPath
+
+*/
+
+qreal QFxPathPercent::value() const
+{
+ return _value;
+}
+
+void QFxPathPercent::setValue(qreal value)
+{
+ _value = value;
+}
+QT_END_NAMESPACE