diff options
Diffstat (limited to 'src/declarative/fx/qfxpath.cpp')
-rw-r--r-- | src/declarative/fx/qfxpath.cpp | 745 |
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 |