aboutsummaryrefslogtreecommitdiffstats
path: root/src/declarative/util/qdeclarativepath.cpp
diff options
context:
space:
mode:
authorMichael Brasser <michael.brasser@nokia.com>2011-09-01 08:47:56 +1000
committerQt by Nokia <qt-info@nokia.com>2011-09-08 00:05:26 +0200
commitfee3fe7d76f1a4556130a097dceb818cc6f33c47 (patch)
tree21bef8d90e8b9fc02f05fefb8f8909faa05fc08e /src/declarative/util/qdeclarativepath.cpp
parentca6d931bb7714de7293dab5d14a19f74dc98d103 (diff)
Add new path functionality.
Introduces new elements PathCurve, PathArc, PathSvg, PathAnimation, and PathInterpolator. Change-Id: I0f5b6284511f332e826ed373018418d2e2a8c07e Reviewed-on: http://codereview.qt-project.org/4220 Reviewed-by: Martin Jones <martin.jones@nokia.com>
Diffstat (limited to 'src/declarative/util/qdeclarativepath.cpp')
-rw-r--r--src/declarative/util/qdeclarativepath.cpp661
1 files changed, 604 insertions, 57 deletions
diff --git a/src/declarative/util/qdeclarativepath.cpp b/src/declarative/util/qdeclarativepath.cpp
index 34752b5242..57387f5ddd 100644
--- a/src/declarative/util/qdeclarativepath.cpp
+++ b/src/declarative/util/qdeclarativepath.cpp
@@ -41,6 +41,7 @@
#include "private/qdeclarativepath_p.h"
#include "private/qdeclarativepath_p_p.h"
+#include "private/qdeclarativesvgparser_p.h"
#include <QSet>
#include <QTime>
@@ -97,35 +98,47 @@ QDeclarativePath::~QDeclarativePath()
qreal QDeclarativePath::startX() const
{
Q_D(const QDeclarativePath);
- return d->startX;
+ return d->startX.isNull ? 0 : d->startX.value;
}
void QDeclarativePath::setStartX(qreal x)
{
Q_D(QDeclarativePath);
- if (qFuzzyCompare(x, d->startX))
+ 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;
+ return d->startY.isNull ? 0 : d->startY.value;
}
void QDeclarativePath::setStartY(qreal y)
{
Q_D(QDeclarativePath);
- if (qFuzzyCompare(y, d->startY))
+ 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.
@@ -163,6 +176,11 @@ QDeclarativeListProperty<QDeclarativePathElement> QDeclarativePath::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;
@@ -170,7 +188,7 @@ void QDeclarativePath::interpolate(int idx, const QString &name, qreal value)
qreal lastPercent = 0;
int search = idx - 1;
while(search >= 0) {
- const AttributePoint &point = d->_attributePoints.at(search);
+ const AttributePoint &point = attributePoints.at(search);
if (point.values.contains(name)) {
lastValue = point.values.value(name);
lastPercent = point.origpercent;
@@ -181,10 +199,10 @@ void QDeclarativePath::interpolate(int idx, const QString &name, qreal value)
++search;
- const AttributePoint &curPoint = d->_attributePoints.at(idx);
+ const AttributePoint &curPoint = attributePoints.at(idx);
for (int ii = search; ii < idx; ++ii) {
- AttributePoint &point = d->_attributePoints[ii];
+ AttributePoint &point = attributePoints[ii];
qreal val = lastValue + (value - lastValue) * (point.origpercent - lastPercent) / (curPoint.origpercent - lastPercent);
point.values.insert(name, val);
@@ -208,6 +226,22 @@ void QDeclarativePath::endpoint(const QString &name)
}
}
+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(QStringLiteral("_qfx_percent"));
void QDeclarativePath::processPath()
@@ -218,42 +252,64 @@ void QDeclarativePath::processPath()
return;
d->_pointCache.clear();
- d->_attributePoints.clear();
- d->_path = QPainterPath();
+ 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 < d->_attributes.count(); ++ii)
- first.values[d->_attributes.at(ii)] = 0;
- d->_attributePoints << first;
+ for (int ii = 0; ii < attributes.count(); ++ii)
+ first.values[attributes.at(ii)] = 0;
+ attributePoints << first;
- d->_path.moveTo(d->startX, d->startY);
+ qreal startX = d->startX.isValid() ? d->startX.value : startPoint.x();
+ qreal startY = d->startY.isValid() ? d->startY.value : startPoint.y();
+ path.moveTo(startX, startY);
- QDeclarativeCurve *lastCurve = 0;
bool usesPercent = false;
+ int index = 0;
foreach (QDeclarativePathElement *pathElement, d->_pathElements) {
if (QDeclarativeCurve *curve = qobject_cast<QDeclarativeCurve *>(pathElement)) {
- curve->addToPath(d->_path);
+ QDeclarativePathData data;
+ data.index = index;
+ data.endPoint = endPoint;
+ data.curves = d->_pathCurves;
+ curve->addToPath(path, data);
AttributePoint p;
- p.origpercent = d->_path.length();
- d->_attributePoints << p;
- lastCurve = curve;
+ p.origpercent = path.length();
+ attributePoints << p;
+ ++index;
} else if (QDeclarativePathAttribute *attribute = qobject_cast<QDeclarativePathAttribute *>(pathElement)) {
- AttributePoint &point = d->_attributePoints.last();
+ AttributePoint &point = attributePoints.last();
point.values[attribute->name()] = attribute->value();
- interpolate(d->_attributePoints.count() - 1, attribute->name(), attribute->value());
+ interpolate(attributePoints, attributePoints.count() - 1, attribute->name(), attribute->value());
} else if (QDeclarativePathPercent *percent = qobject_cast<QDeclarativePathPercent *>(pathElement)) {
- AttributePoint &point = d->_attributePoints.last();
+ AttributePoint &point = attributePoints.last();
point.values[percentString] = percent->value();
- interpolate(d->_attributePoints.count() - 1, percentString, percent->value());
+ interpolate(attributePoints, attributePoints.count() - 1, percentString, percent->value());
usesPercent = true;
}
}
// 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));
+ 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;
@@ -262,30 +318,34 @@ void QDeclarativePath::processPath()
// Adjust percent
- qreal length = d->_path.length();
+ qreal length = path.length();
qreal prevpercent = 0;
qreal prevorigpercent = 0;
- for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
- const AttributePoint &point = d->_attributePoints.at(ii);
+ 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 = (d->_attributePoints[ii].origpercent/length - prevorigpercent) /
+ qreal scale = (attributePoints[ii].origpercent/length - prevorigpercent) /
(point.values.value(percentString)-prevpercent);
- d->_attributePoints[ii].scale = scale;
+ attributePoints[ii].scale = scale;
}
- d->_attributePoints[ii].origpercent /= length;
- d->_attributePoints[ii].percent = point.values.value(percentString);
- prevorigpercent = d->_attributePoints[ii].origpercent;
- prevpercent = d->_attributePoints[ii].percent;
+ attributePoints[ii].origpercent /= length;
+ attributePoints[ii].percent = point.values.value(percentString);
+ prevorigpercent = attributePoints[ii].origpercent;
+ prevpercent = attributePoints[ii].percent;
} else {
- d->_attributePoints[ii].origpercent /= length;
- d->_attributePoints[ii].percent = d->_attributePoints[ii].origpercent;
+ attributePoints[ii].origpercent /= length;
+ attributePoints[ii].percent = attributePoints[ii].origpercent;
}
}
- d->closed = lastCurve && d->startX == lastCurve->x() && d->startY == lastCurve->y();
+ if (closed) {
+ QPointF end = path.currentPosition();
+ *closed = length > 0 && startX == end.x() && startY == end.y();
+ }
+ pathLength = length;
- emit changed();
+ return path;
}
void QDeclarativePath::classBegin()
@@ -302,8 +362,11 @@ void QDeclarativePath::componentComplete()
// First gather up all the attributes
foreach (QDeclarativePathElement *pathElement, d->_pathElements) {
- if (QDeclarativePathAttribute *attribute =
- qobject_cast<QDeclarativePathAttribute *>(pathElement))
+ 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();
@@ -337,10 +400,10 @@ QStringList QDeclarativePath::attributes() const
return d->_attributes;
}
-static inline QBezier nextBezier(const QPainterPath &path, int *from, qreal *bezLength)
+static inline QBezier nextBezier(const QPainterPath &path, int *from, qreal *bezLength, bool reverse = false)
{
- const int lastElement = path.elementCount() - 1;
- for (int i=*from; i <= lastElement; ++i) {
+ const int lastElement = reverse ? 0 : path.elementCount() - 1;
+ for (int i=*from; reverse ? i >= lastElement : i <= lastElement; reverse ? --i : ++i) {
const QPainterPath::Element &e = path.elementAt(i);
switch (e.type) {
@@ -352,7 +415,7 @@ static inline QBezier nextBezier(const QPainterPath &path, int *from, qreal *bez
*bezLength = line.length();
QPointF a = path.elementAt(i-1);
QPointF delta = e - a;
- *from = i+1;
+ *from = reverse ? i-1 : i+1;
return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
}
case QPainterPath::CurveToElement:
@@ -362,7 +425,7 @@ static inline QBezier nextBezier(const QPainterPath &path, int *from, qreal *bez
path.elementAt(i+1),
path.elementAt(i+2));
*bezLength = b.length();
- *from = i+3;
+ *from = reverse ? i-1 : i+3;
return b;
}
default:
@@ -374,10 +437,16 @@ static inline QBezier nextBezier(const QPainterPath &path, int *from, qreal *bez
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->_path.length();
+ qreal pathLength = d->pathLength;
if (pathLength <= 0 || qIsNaN(pathLength))
return;
// more points means less jitter between items as they move along the
@@ -426,6 +495,140 @@ void QDeclarativePath::createPointCache() const
}
}
+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 : 0;
+ 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) {
+ if (currElement > lastElement)
+ break;
+ currBez = nextBezier(path, &currElement, &bezLength);
+ /*if (bezLength == 0.0) {
+ currLength = pathLength;
+ epc = 1.0;
+ break;
+ }*/
+ 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() - 1;
+ 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) {
+ if (currElement < firstElement)
+ break;
+ currBez = nextBezier(path, &currElement, &bezLength, true /*reverse*/);
+ /*if (bezLength == 0.0) {
+ currLength = 0;
+ epc = 0.0;
+ break;
+ }*/
+ currLength = prevLength;
+ epc = (currLength - bezLength) / 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);
@@ -472,32 +675,80 @@ qreal QDeclarativePath::attributeAt(const QString &name, qreal percent) const
qreal QDeclarativeCurve::x() const
{
- return _x;
+ return _x.isNull ? 0 : _x.value;
}
void QDeclarativeCurve::setX(qreal x)
{
- if (_x != x) {
+ if (_x.isNull || _x != x) {
_x = x;
emit xChanged();
emit changed();
}
}
+bool QDeclarativeCurve::hasX()
+{
+ return _x.isValid();
+}
+
qreal QDeclarativeCurve::y() const
{
- return _y;
+ return _y.isNull ? 0 : _y.value;
}
void QDeclarativeCurve::setY(qreal y)
{
- if (_y != 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();
+}
+
/****************************************************************************/
/*!
@@ -642,9 +893,17 @@ void QDeclarativePathAttribute::setValue(qreal value)
Defines the end point of the line.
*/
-void QDeclarativePathLine::addToPath(QPainterPath &path)
+inline QPointF positionForCurve(const QDeclarativePathData &data, const QPointF &prevPoint)
{
- path.lineTo(x(), y());
+ 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()));
}
/****************************************************************************/
@@ -720,9 +979,50 @@ void QDeclarativePathQuad::setControlY(qreal y)
}
}
-void QDeclarativePathQuad::addToPath(QPainterPath &path)
+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()
{
- path.quadTo(controlX(), controlY(), x(), y());
+ 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()));
}
/****************************************************************************/
@@ -828,9 +1128,256 @@ void QDeclarativePathCubic::setControl2Y(qreal y)
}
}
-void QDeclarativePathCubic::addToPath(QPainterPath &path)
+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 &)
{
- path.cubicTo(control1X(), control1Y(), control2X(), control2Y(), x(), y());
+ QDeclarativeSvgParser::parsePathDataFast(_path, path);
}
/****************************************************************************/