Skip to content

Commit

Permalink
PathAnimation updates.
Browse files Browse the repository at this point in the history
Allow smooth orientation changes, smooth interruptions,
and implicit "from" in PathAnimation.

Change-Id: I2191f6df89ec25d78b1d498827281803a07129c9
Reviewed-on: http://codereview.qt-project.org/4378
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Martin Jones <martin.jones@nokia.com>
  • Loading branch information
Michael Brasser authored and Qt by Nokia committed Sep 18, 2011
1 parent 38fc46c commit 48e3335
Show file tree
Hide file tree
Showing 9 changed files with 351 additions and 62 deletions.
148 changes: 135 additions & 13 deletions src/declarative/items/qsganimation.cpp
Expand Up @@ -554,6 +554,53 @@ void QSGPathAnimation::setAnchorPoint(const QPointF &point)
emit anchorPointChanged(point);
}

qreal QSGPathAnimation::orientationEntryInterval() const
{
Q_D(const QSGPathAnimation);
return d->entryInterval;
}

void QSGPathAnimation::setOrientationEntryInterval(qreal interval)
{
Q_D(QSGPathAnimation);
if (d->entryInterval == interval)
return;
d->entryInterval = interval;
emit orientationEntryIntervalChanged(interval);
}

qreal QSGPathAnimation::orientationExitInterval() const
{
Q_D(const QSGPathAnimation);
return d->exitInterval;
}

void QSGPathAnimation::setOrientationExitInterval(qreal interval)
{
Q_D(QSGPathAnimation);
if (d->exitInterval == interval)
return;
d->exitInterval = interval;
emit orientationExitIntervalChanged(interval);
}

qreal QSGPathAnimation::endRotation() const
{
Q_D(const QSGPathAnimation);
return d->endRotation.isNull ? qreal(0) : d->endRotation.value;
}

void QSGPathAnimation::setEndRotation(qreal rotation)
{
Q_D(QSGPathAnimation);
if (!d->endRotation.isNull && d->endRotation == rotation)
return;

d->endRotation = rotation;
emit endRotationChanged(d->endRotation);
}


QAbstractAnimation *QSGPathAnimation::qtAnimation()
{
Q_D(QSGPathAnimation);
Expand All @@ -569,9 +616,13 @@ void QSGPathAnimation::transition(QDeclarativeStateActions &actions,

data->orientation = d->orientation;
data->anchorPoint = d->anchorPoint;
data->entryInterval = d->entryInterval;
data->exitInterval = d->exitInterval;
data->endRotation = d->endRotation;
data->reverse = direction == Backward ? true : false;
data->fromSourced = false;
data->fromDefined = d->path ? !d->path->hasStartX() || !d->path->hasStartY() ? false : true : false; //### handle x/y separately, as well as endpoint specification?
data->fromDefined = (d->path && d->path->hasStartX() && d->path->hasStartY()) ? true : false;
data->toDefined = d->path ? d->path->hasEnd() : false;
int origModifiedSize = modified.count();

for (int i = 0; i < actions.count(); ++i) {
Expand All @@ -591,14 +642,42 @@ void QSGPathAnimation::transition(QDeclarativeStateActions &actions,
}

if (d->target && d->path &&
(modified.count() > origModifiedSize || data->fromDefined)) {
(modified.count() > origModifiedSize || data->toDefined)) {
data->target = d->target;
data->path = d->path;
if (!d->rangeIsSet) {
d->pa->setStartValue(qreal(0));
d->pa->setEndValue(qreal(1));
d->rangeIsSet = true;
}
/*
NOTE: The following block relies on the fact that the previous value hasn't
yet been deleted, and has the same target, etc, which may be a bit fragile.
*/
if (d->pa->getAnimValue()) {
QSGPathAnimationUpdater *prevData = static_cast<QSGPathAnimationUpdater*>(d->pa->getAnimValue());

// get the original start angle that was used (so we can exactly reverse).
data->startRotation = prevData->startRotation;

// treat interruptions specially, otherwise we end up with strange paths
if ((data->reverse || prevData->reverse) && prevData->currentV > 0 && prevData->currentV < 1) {
if (!data->fromDefined && !data->toDefined && !prevData->painterPath.isEmpty()) {
QPointF pathPos = QDeclarativePath::sequentialPointAt(prevData->painterPath, prevData->pathLength, prevData->attributePoints, prevData->prevBez, prevData->currentV);
if (!prevData->anchorPoint.isNull())
pathPos -= prevData->anchorPoint;
if (pathPos == data->target->pos()) { //only treat as interruption if we interrupted ourself
data->painterPath = prevData->painterPath;
data->toDefined = data->fromDefined = data->fromSourced = true;
data->prevBez.isValid = false;
data->interruptStart = prevData->currentV;
data->startRotation = prevData->startRotation;
data->pathLength = prevData->pathLength;
data->attributePoints = prevData->attributePoints;
}
}
}
}
d->pa->setFromSourcedValue(&data->fromSourced);
d->pa->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped);
} else {
Expand All @@ -610,7 +689,15 @@ void QSGPathAnimation::transition(QDeclarativeStateActions &actions,

void QSGPathAnimationUpdater::setValue(qreal v)
{
if (!fromSourced && !fromDefined) { //### check if !toDefined?
if (interruptStart.isValid()) {
if (reverse)
v = 1 - v;
qreal end = reverse ? 0.0 : 1.0;
v = interruptStart + v * (end-interruptStart);
}
currentV = v;
bool atStart = ((reverse && v == 1.0) || (!reverse && v == 0.0));
if (!fromSourced && (!fromDefined || !toDefined)) {
qreal startX = reverse ? toX + anchorPoint.x() : target->x() + anchorPoint.x();
qreal startY = reverse ? toY + anchorPoint.y() : target->y() + anchorPoint.y();
qreal endX = reverse ? target->x() + anchorPoint.x() : toX + anchorPoint.x();
Expand All @@ -628,13 +715,13 @@ void QSGPathAnimationUpdater::setValue(qreal v)
//adjust position according to anchor point
if (!anchorPoint.isNull()) {
currentPos -= anchorPoint;
if ((reverse && v == 1.0) || (!reverse && v == 0.0)) {
if (atStart) {
if (!anchorPoint.isNull() && !fixed)
target->setTransformOriginPoint(anchorPoint);
}
}

//### too expensive to reconstruct properties each time?
//### could cache properties rather than reconstructing each time
QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, "x"), currentPos.x(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, "y"), currentPos.y(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);

Expand All @@ -644,27 +731,62 @@ void QSGPathAnimationUpdater::setValue(qreal v)
case QSGPathAnimation::RightFirst:
angle = -angle;
break;
case QSGPathAnimation::TopFirst:
angle = -angle + 90;
break;
case QSGPathAnimation::LeftFirst:
angle = -angle + 180;
break;
case QSGPathAnimation::BottomFirst:
angle = -angle + 270;
break;
case QSGPathAnimation::TopFirst:
angle = -angle + 450;
break;
default:
angle = 0;
break;
}

if (atStart && !reverse) {
startRotation = target->rotation();

//shortest distance to correct orientation
qreal diff = angle - startRotation;
while (diff > 180.0) {
startRotation.value += 360.0;
diff -= 360.0;
}
while (diff < -180.0) {
startRotation.value -= 360.0;
diff += 360.0;
}
}

//smoothly transition to the desired orientation
if (startRotation.isValid()) {
if (reverse && v == 0.0)
angle = startRotation;
else if (v < entryInterval)
angle = angle * v / entryInterval + startRotation * (entryInterval - v) / entryInterval;
}
if (endRotation.isValid()) {
qreal exitStart = 1 - exitInterval;
if (!reverse && v == 1.0)
angle = endRotation;
else if (v > exitStart)
angle = endRotation * (v - exitStart) / exitInterval + angle * (exitInterval - (v - exitStart)) / exitInterval;
}
QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, "rotation"), angle, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
}

//### resetting transform causes visual jump if ending on an angle
// if ((reverse && v == 0.0) || (!reverse && v == 1.0)) {
// if (!anchorPoint.isNull() && !fixed)
// target->setTransformOriginPoint(QPointF());
// }
/*
NOTE: we don't always reset the transform origin, as it can cause a
visual jump if ending on an angle. This means that in some cases
(anchor point and orientation both specified, and ending at an angle)
the transform origin will always be set after running the path animation.
*/
if ((reverse && v == 0.0) || (!reverse && v == 1.0)) {
if (!anchorPoint.isNull() && !fixed && qFuzzyIsNull(angle))
target->setTransformOriginPoint(QPointF());
}
}

QT_END_NAMESPACE
15 changes: 15 additions & 0 deletions src/declarative/items/qsganimation_p.h
Expand Up @@ -136,6 +136,9 @@ class Q_AUTOTEST_EXPORT QSGPathAnimation : public QDeclarativeAbstractAnimation
Q_PROPERTY(QSGItem *target READ target WRITE setTarget NOTIFY targetChanged)
Q_PROPERTY(Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged)
Q_PROPERTY(QPointF anchorPoint READ anchorPoint WRITE setAnchorPoint NOTIFY anchorPointChanged)
Q_PROPERTY(qreal orientationEntryInterval READ orientationEntryInterval WRITE setOrientationEntryInterval NOTIFY orientationEntryIntervalChanged)
Q_PROPERTY(qreal orientationExitInterval READ orientationExitInterval WRITE setOrientationExitInterval NOTIFY orientationExitIntervalChanged)
Q_PROPERTY(qreal endRotation READ endRotation WRITE setEndRotation NOTIFY endRotationChanged)

public:
QSGPathAnimation(QObject *parent=0);
Expand Down Expand Up @@ -168,6 +171,15 @@ class Q_AUTOTEST_EXPORT QSGPathAnimation : public QDeclarativeAbstractAnimation
QPointF anchorPoint() const;
void setAnchorPoint(const QPointF &point);

qreal orientationEntryInterval() const;
void setOrientationEntryInterval(qreal);

qreal orientationExitInterval() const;
void setOrientationExitInterval(qreal);

qreal endRotation() const;
void setEndRotation(qreal);

protected:
virtual void transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
Expand All @@ -181,6 +193,9 @@ class Q_AUTOTEST_EXPORT QSGPathAnimation : public QDeclarativeAbstractAnimation
void targetChanged();
void orientationChanged(Orientation);
void anchorPointChanged(const QPointF &);
void orientationEntryIntervalChanged(qreal);
void orientationExitIntervalChanged(qreal);
void endRotationChanged(qreal);
};

QT_END_NAMESPACE
Expand Down
25 changes: 20 additions & 5 deletions src/declarative/items/qsganimation_p_p.h
Expand Up @@ -96,6 +96,14 @@ class QSGAnchorAnimationPrivate : public QDeclarativeAbstractAnimationPrivate
class QSGPathAnimationUpdater : public QDeclarativeBulkValueUpdater
{
public:
QSGPathAnimationUpdater() : path(0), target(0), reverse(false),
fromSourced(false), fromDefined(false), toDefined(false),
toX(0), toY(0), currentV(0), orientation(QSGPathAnimation::Fixed),
entryInterval(0), exitInterval(0) {}
~QSGPathAnimationUpdater() {}

void setValue(qreal v);

QDeclarativePath *path;

QPainterPath painterPath;
Expand All @@ -107,28 +115,35 @@ class QSGPathAnimationUpdater : public QDeclarativeBulkValueUpdater
bool reverse;
bool fromSourced;
bool fromDefined;
bool toDefined;
qreal toX;
qreal toY;
qreal currentV;
QDeclarativeNullableValue<qreal> interruptStart;
//TODO: bundle below into common struct
QSGPathAnimation::Orientation orientation;
QPointF anchorPoint;
QSGPathAnimationUpdater() : path(0), target(0), reverse(false),
fromSourced(false), fromDefined(false), toX(0), toY(0), orientation(QSGPathAnimation::Fixed) {}
~QSGPathAnimationUpdater() {}
void setValue(qreal v);
qreal entryInterval;
qreal exitInterval;
QDeclarativeNullableValue<qreal> endRotation;
QDeclarativeNullableValue<qreal> startRotation;
};

class QSGPathAnimationPrivate : public QDeclarativeAbstractAnimationPrivate
{
Q_DECLARE_PUBLIC(QSGPathAnimation)
public:
QSGPathAnimationPrivate() : path(0), target(0),
rangeIsSet(false), orientation(QSGPathAnimation::Fixed), pa(0) {}
rangeIsSet(false), orientation(QSGPathAnimation::Fixed), entryInterval(0), exitInterval(0), pa(0) {}

QDeclarativePath *path;
QSGItem *target;
bool rangeIsSet;
QSGPathAnimation::Orientation orientation;
QPointF anchorPoint;
qreal entryInterval;
qreal exitInterval;
QDeclarativeNullableValue<qreal> endRotation;
QDeclarativeBulkValueAnimator *pa;
};

Expand Down
4 changes: 4 additions & 0 deletions src/declarative/util/qdeclarativeanimation_p_p.h
Expand Up @@ -156,6 +156,10 @@ class Q_AUTOTEST_EXPORT QDeclarativeBulkValueAnimator : public QVariantAnimation
animValue = value;
policy = p;
}
QDeclarativeBulkValueUpdater *getAnimValue() const
{
return animValue;
}
void setFromSourcedValue(bool *value)
{
fromSourced = value;
Expand Down

0 comments on commit 48e3335

Please sign in to comment.