1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickanimation_p.h"
43 #include "qquickanimation_p_p.h"
44 #include "qquickstateoperations_p.h"
46 #include <private/qdeclarativeproperty_p.h>
47 #include <private/qdeclarativepath_p.h>
49 #include <QtDeclarative/qdeclarativeinfo.h>
50 #include <QtCore/qmath.h>
51 #include <QtCore/qsequentialanimationgroup.h>
52 #include <QtCore/qparallelanimationgroup.h>
53 #include <QtGui/qtransform.h>
57 QQuickParentAnimation::QQuickParentAnimation(QObject *parent)
58 : QDeclarativeAnimationGroup(*(new QQuickParentAnimationPrivate), parent)
60 Q_D(QQuickParentAnimation);
61 d->topLevelGroup = new QSequentialAnimationGroup;
62 QDeclarative_setParent_noEvent(d->topLevelGroup, this);
64 d->startAction = new QActionAnimation;
65 QDeclarative_setParent_noEvent(d->startAction, d->topLevelGroup);
66 d->topLevelGroup->addAnimation(d->startAction);
68 d->ag = new QParallelAnimationGroup;
69 QDeclarative_setParent_noEvent(d->ag, d->topLevelGroup);
70 d->topLevelGroup->addAnimation(d->ag);
72 d->endAction = new QActionAnimation;
73 QDeclarative_setParent_noEvent(d->endAction, d->topLevelGroup);
74 d->topLevelGroup->addAnimation(d->endAction);
77 QQuickParentAnimation::~QQuickParentAnimation()
81 QQuickItem *QQuickParentAnimation::target() const
83 Q_D(const QQuickParentAnimation);
87 void QQuickParentAnimation::setTarget(QQuickItem *target)
89 Q_D(QQuickParentAnimation);
90 if (target == d->target)
97 QQuickItem *QQuickParentAnimation::newParent() const
99 Q_D(const QQuickParentAnimation);
103 void QQuickParentAnimation::setNewParent(QQuickItem *newParent)
105 Q_D(QQuickParentAnimation);
106 if (newParent == d->newParent)
109 d->newParent = newParent;
110 emit newParentChanged();
113 QQuickItem *QQuickParentAnimation::via() const
115 Q_D(const QQuickParentAnimation);
119 void QQuickParentAnimation::setVia(QQuickItem *via)
121 Q_D(QQuickParentAnimation);
129 //### mirrors same-named function in QQuickItem
130 QPointF QQuickParentAnimationPrivate::computeTransformOrigin(QQuickItem::TransformOrigin origin, qreal width, qreal height) const
134 case QQuickItem::TopLeft:
135 return QPointF(0, 0);
136 case QQuickItem::Top:
137 return QPointF(width / 2., 0);
138 case QQuickItem::TopRight:
139 return QPointF(width, 0);
140 case QQuickItem::Left:
141 return QPointF(0, height / 2.);
142 case QQuickItem::Center:
143 return QPointF(width / 2., height / 2.);
144 case QQuickItem::Right:
145 return QPointF(width, height / 2.);
146 case QQuickItem::BottomLeft:
147 return QPointF(0, height);
148 case QQuickItem::Bottom:
149 return QPointF(width / 2., height);
150 case QQuickItem::BottomRight:
151 return QPointF(width, height);
155 void QQuickParentAnimation::transition(QDeclarativeStateActions &actions,
156 QDeclarativeProperties &modified,
157 TransitionDirection direction)
159 Q_D(QQuickParentAnimation);
161 struct QQuickParentAnimationData : public QAbstractAnimationAction
163 QQuickParentAnimationData() {}
164 ~QQuickParentAnimationData() { qDeleteAll(pc); }
166 QDeclarativeStateActions actions;
167 //### reverse should probably apply on a per-action basis
169 QList<QQuickParentChange *> pc;
170 virtual void doAction()
172 for (int ii = 0; ii < actions.count(); ++ii) {
173 const QDeclarativeAction &action = actions.at(ii);
175 action.event->reverse();
177 action.event->execute();
182 QQuickParentAnimationData *data = new QQuickParentAnimationData;
183 QQuickParentAnimationData *viaData = new QQuickParentAnimationData;
185 bool hasExplicit = false;
186 if (d->target && d->newParent) {
187 data->reverse = false;
188 QDeclarativeAction myAction;
189 QQuickParentChange *pc = new QQuickParentChange;
190 pc->setObject(d->target);
191 pc->setParent(d->newParent);
194 data->actions << myAction;
197 viaData->reverse = false;
198 QDeclarativeAction myVAction;
199 QQuickParentChange *vpc = new QQuickParentChange;
200 vpc->setObject(d->target);
201 vpc->setParent(d->via);
202 myVAction.event = vpc;
204 viaData->actions << myVAction;
206 //### once actions have concept of modified,
207 // loop to match appropriate ParentChanges and mark as modified
211 for (int i = 0; i < actions.size(); ++i) {
212 QDeclarativeAction &action = actions[i];
213 if (action.event && action.event->typeName() == QLatin1String("ParentChange")
214 && (!d->target || static_cast<QQuickParentChange*>(action.event)->object() == d->target)) {
216 QQuickParentChange *pc = static_cast<QQuickParentChange*>(action.event);
217 QDeclarativeAction myAction = action;
218 data->reverse = action.reverseEvent;
220 //### this logic differs from PropertyAnimation
221 // (probably a result of modified vs. done)
223 QQuickParentChange *epc = new QQuickParentChange;
224 epc->setObject(static_cast<QQuickParentChange*>(action.event)->object());
225 epc->setParent(d->newParent);
226 myAction.event = epc;
228 data->actions << myAction;
231 action.actionDone = true;
232 data->actions << myAction;
236 viaData->reverse = false;
237 QDeclarativeAction myAction;
238 QQuickParentChange *vpc = new QQuickParentChange;
239 vpc->setObject(pc->object());
240 vpc->setParent(d->via);
241 myAction.event = vpc;
243 viaData->actions << myAction;
244 QDeclarativeAction dummyAction;
245 QDeclarativeAction &xAction = pc->xIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
246 QDeclarativeAction &yAction = pc->yIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
247 QDeclarativeAction &sAction = pc->scaleIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
248 QDeclarativeAction &rAction = pc->rotationIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
249 QQuickItem *target = pc->object();
250 QQuickItem *targetParent = action.reverseEvent ? pc->originalParent() : pc->parent();
252 //### this mirrors the logic in QQuickParentChange.
254 const QTransform &transform = targetParent->itemTransform(d->via, &ok);
255 if (transform.type() >= QTransform::TxShear || !ok) {
256 qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under complex transform");
262 bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0);
263 if (ok && !isRotate) {
264 if (transform.m11() == transform.m22())
265 scale = transform.m11();
267 qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under non-uniform scale");
270 } else if (ok && isRotate) {
271 if (transform.m11() == transform.m22())
272 scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12());
274 qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under non-uniform scale");
279 rotation = atan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI;
281 qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under scale of 0");
286 const QPointF &point = transform.map(QPointF(xAction.toValue.toReal(),yAction.toValue.toReal()));
289 if (ok && target->transformOrigin() != QQuickItem::TopLeft) {
290 qreal w = target->width();
291 qreal h = target->height();
292 if (pc->widthIsSet() && i < actions.size() - 1)
293 w = actions[++i].toValue.toReal();
294 if (pc->heightIsSet() && i < actions.size() - 1)
295 h = actions[++i].toValue.toReal();
296 const QPointF &transformOrigin
297 = d->computeTransformOrigin(target->transformOrigin(), w,h);
298 qreal tempxt = transformOrigin.x();
299 qreal tempyt = transformOrigin.y();
301 t.translate(-tempxt, -tempyt);
303 t.scale(scale, scale);
304 t.translate(tempxt, tempyt);
305 const QPointF &offset = t.map(QPointF(0,0));
311 //qDebug() << x << y << rotation << scale;
314 sAction.toValue = sAction.toValue.toReal() * scale;
315 rAction.toValue = rAction.toValue.toReal() + rotation;
321 if (data->actions.count()) {
322 if (direction == QDeclarativeAbstractAnimation::Forward) {
323 d->startAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped);
324 d->endAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped);
326 d->endAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped);
327 d->startAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped);
334 //take care of any child animations
335 bool valid = d->defaultProperty.isValid();
336 for (int ii = 0; ii < d->animations.count(); ++ii) {
338 d->animations.at(ii)->setDefaultTarget(d->defaultProperty);
339 d->animations.at(ii)->transition(actions, modified, direction);
344 QAbstractAnimation *QQuickParentAnimation::qtAnimation()
346 Q_D(QQuickParentAnimation);
347 return d->topLevelGroup;
350 QQuickAnchorAnimation::QQuickAnchorAnimation(QObject *parent)
351 : QDeclarativeAbstractAnimation(*(new QQuickAnchorAnimationPrivate), parent)
353 Q_D(QQuickAnchorAnimation);
354 d->va = new QDeclarativeBulkValueAnimator;
355 QDeclarative_setParent_noEvent(d->va, this);
358 QQuickAnchorAnimation::~QQuickAnchorAnimation()
362 QAbstractAnimation *QQuickAnchorAnimation::qtAnimation()
364 Q_D(QQuickAnchorAnimation);
368 QDeclarativeListProperty<QQuickItem> QQuickAnchorAnimation::targets()
370 Q_D(QQuickAnchorAnimation);
371 return QDeclarativeListProperty<QQuickItem>(this, d->targets);
374 int QQuickAnchorAnimation::duration() const
376 Q_D(const QQuickAnchorAnimation);
377 return d->va->duration();
380 void QQuickAnchorAnimation::setDuration(int duration)
383 qmlInfo(this) << tr("Cannot set a duration of < 0");
387 Q_D(QQuickAnchorAnimation);
388 if (d->va->duration() == duration)
390 d->va->setDuration(duration);
391 emit durationChanged(duration);
394 QEasingCurve QQuickAnchorAnimation::easing() const
396 Q_D(const QQuickAnchorAnimation);
397 return d->va->easingCurve();
400 void QQuickAnchorAnimation::setEasing(const QEasingCurve &e)
402 Q_D(QQuickAnchorAnimation);
403 if (d->va->easingCurve() == e)
406 d->va->setEasingCurve(e);
407 emit easingChanged(e);
410 void QQuickAnchorAnimation::transition(QDeclarativeStateActions &actions,
411 QDeclarativeProperties &modified,
412 TransitionDirection direction)
415 Q_D(QQuickAnchorAnimation);
416 QDeclarativeAnimationPropertyUpdater *data = new QDeclarativeAnimationPropertyUpdater;
417 data->interpolatorType = QMetaType::QReal;
418 data->interpolator = d->interpolator;
420 data->reverse = direction == Backward ? true : false;
421 data->fromSourced = false;
422 data->fromDefined = false;
424 for (int ii = 0; ii < actions.count(); ++ii) {
425 QDeclarativeAction &action = actions[ii];
426 if (action.event && action.event->typeName() == QLatin1String("AnchorChanges")
427 && (d->targets.isEmpty() || d->targets.contains(static_cast<QQuickAnchorChanges*>(action.event)->object()))) {
428 data->actions << static_cast<QQuickAnchorChanges*>(action.event)->additionalActions();
432 if (data->actions.count()) {
433 if (!d->rangeIsSet) {
434 d->va->setStartValue(qreal(0));
435 d->va->setEndValue(qreal(1));
436 d->rangeIsSet = true;
438 d->va->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped);
439 d->va->setFromSourcedValue(&data->fromSourced);
445 QQuickPathAnimation::QQuickPathAnimation(QObject *parent)
446 : QDeclarativeAbstractAnimation(*(new QQuickPathAnimationPrivate), parent)
448 Q_D(QQuickPathAnimation);
449 d->pa = new QDeclarativeBulkValueAnimator;
450 QDeclarative_setParent_noEvent(d->pa, this);
453 QQuickPathAnimation::~QQuickPathAnimation()
457 int QQuickPathAnimation::duration() const
459 Q_D(const QQuickPathAnimation);
460 return d->pa->duration();
463 void QQuickPathAnimation::setDuration(int duration)
466 qmlInfo(this) << tr("Cannot set a duration of < 0");
470 Q_D(QQuickPathAnimation);
471 if (d->pa->duration() == duration)
473 d->pa->setDuration(duration);
474 emit durationChanged(duration);
477 QEasingCurve QQuickPathAnimation::easing() const
479 Q_D(const QQuickPathAnimation);
480 return d->pa->easingCurve();
483 void QQuickPathAnimation::setEasing(const QEasingCurve &e)
485 Q_D(QQuickPathAnimation);
486 if (d->pa->easingCurve() == e)
489 d->pa->setEasingCurve(e);
490 emit easingChanged(e);
493 QDeclarativePath *QQuickPathAnimation::path() const
495 Q_D(const QQuickPathAnimation);
499 void QQuickPathAnimation::setPath(QDeclarativePath *path)
501 Q_D(QQuickPathAnimation);
509 QQuickItem *QQuickPathAnimation::target() const
511 Q_D(const QQuickPathAnimation);
515 void QQuickPathAnimation::setTarget(QQuickItem *target)
517 Q_D(QQuickPathAnimation);
518 if (d->target == target)
522 emit targetChanged();
525 QQuickPathAnimation::Orientation QQuickPathAnimation::orientation() const
527 Q_D(const QQuickPathAnimation);
528 return d->orientation;
531 void QQuickPathAnimation::setOrientation(Orientation orientation)
533 Q_D(QQuickPathAnimation);
534 if (d->orientation == orientation)
537 d->orientation = orientation;
538 emit orientationChanged(d->orientation);
541 QPointF QQuickPathAnimation::anchorPoint() const
543 Q_D(const QQuickPathAnimation);
544 return d->anchorPoint;
547 void QQuickPathAnimation::setAnchorPoint(const QPointF &point)
549 Q_D(QQuickPathAnimation);
550 if (d->anchorPoint == point)
553 d->anchorPoint = point;
554 emit anchorPointChanged(point);
557 qreal QQuickPathAnimation::orientationEntryInterval() const
559 Q_D(const QQuickPathAnimation);
560 return d->entryInterval;
563 void QQuickPathAnimation::setOrientationEntryInterval(qreal interval)
565 Q_D(QQuickPathAnimation);
566 if (d->entryInterval == interval)
568 d->entryInterval = interval;
569 emit orientationEntryIntervalChanged(interval);
572 qreal QQuickPathAnimation::orientationExitInterval() const
574 Q_D(const QQuickPathAnimation);
575 return d->exitInterval;
578 void QQuickPathAnimation::setOrientationExitInterval(qreal interval)
580 Q_D(QQuickPathAnimation);
581 if (d->exitInterval == interval)
583 d->exitInterval = interval;
584 emit orientationExitIntervalChanged(interval);
587 qreal QQuickPathAnimation::endRotation() const
589 Q_D(const QQuickPathAnimation);
590 return d->endRotation.isNull ? qreal(0) : d->endRotation.value;
593 void QQuickPathAnimation::setEndRotation(qreal rotation)
595 Q_D(QQuickPathAnimation);
596 if (!d->endRotation.isNull && d->endRotation == rotation)
599 d->endRotation = rotation;
600 emit endRotationChanged(d->endRotation);
604 QAbstractAnimation *QQuickPathAnimation::qtAnimation()
606 Q_D(QQuickPathAnimation);
610 void QQuickPathAnimation::transition(QDeclarativeStateActions &actions,
611 QDeclarativeProperties &modified,
612 TransitionDirection direction)
614 Q_D(QQuickPathAnimation);
615 QQuickPathAnimationUpdater *data = new QQuickPathAnimationUpdater;
617 data->orientation = d->orientation;
618 data->anchorPoint = d->anchorPoint;
619 data->entryInterval = d->entryInterval;
620 data->exitInterval = d->exitInterval;
621 data->endRotation = d->endRotation;
622 data->reverse = direction == Backward ? true : false;
623 data->fromSourced = false;
624 data->fromDefined = (d->path && d->path->hasStartX() && d->path->hasStartY()) ? true : false;
625 data->toDefined = d->path ? d->path->hasEnd() : false;
626 int origModifiedSize = modified.count();
628 for (int i = 0; i < actions.count(); ++i) {
629 QDeclarativeAction &action = actions[i];
632 if (action.specifiedObject == d->target && action.property.name() == QLatin1String("x")) {
633 data->toX = action.toValue.toReal();
634 modified << action.property;
635 action.fromValue = action.toValue;
637 if (action.specifiedObject == d->target && action.property.name() == QLatin1String("y")) {
638 data->toY = action.toValue.toReal();
639 modified << action.property;
640 action.fromValue = action.toValue;
644 if (d->target && d->path &&
645 (modified.count() > origModifiedSize || data->toDefined)) {
646 data->target = d->target;
647 data->path = d->path;
648 if (!d->rangeIsSet) {
649 d->pa->setStartValue(qreal(0));
650 d->pa->setEndValue(qreal(1));
651 d->rangeIsSet = true;
654 NOTE: The following block relies on the fact that the previous value hasn't
655 yet been deleted, and has the same target, etc, which may be a bit fragile.
657 if (d->pa->getAnimValue()) {
658 QQuickPathAnimationUpdater *prevData = static_cast<QQuickPathAnimationUpdater*>(d->pa->getAnimValue());
660 // get the original start angle that was used (so we can exactly reverse).
661 data->startRotation = prevData->startRotation;
663 // treat interruptions specially, otherwise we end up with strange paths
664 if ((data->reverse || prevData->reverse) && prevData->currentV > 0 && prevData->currentV < 1) {
665 if (!data->fromDefined && !data->toDefined && !prevData->painterPath.isEmpty()) {
666 QPointF pathPos = QDeclarativePath::sequentialPointAt(prevData->painterPath, prevData->pathLength, prevData->attributePoints, prevData->prevBez, prevData->currentV);
667 if (!prevData->anchorPoint.isNull())
668 pathPos -= prevData->anchorPoint;
669 if (pathPos == data->target->pos()) { //only treat as interruption if we interrupted ourself
670 data->painterPath = prevData->painterPath;
671 data->toDefined = data->fromDefined = data->fromSourced = true;
672 data->prevBez.isValid = false;
673 data->interruptStart = prevData->currentV;
674 data->startRotation = prevData->startRotation;
675 data->pathLength = prevData->pathLength;
676 data->attributePoints = prevData->attributePoints;
681 d->pa->setFromSourcedValue(&data->fromSourced);
682 d->pa->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped);
684 d->pa->setFromSourcedValue(0);
685 d->pa->setAnimValue(0, QAbstractAnimation::DeleteWhenStopped);
690 void QQuickPathAnimationUpdater::setValue(qreal v)
692 if (interruptStart.isValid()) {
695 qreal end = reverse ? 0.0 : 1.0;
696 v = interruptStart + v * (end-interruptStart);
699 bool atStart = ((reverse && v == 1.0) || (!reverse && v == 0.0));
700 if (!fromSourced && (!fromDefined || !toDefined)) {
701 qreal startX = reverse ? toX + anchorPoint.x() : target->x() + anchorPoint.x();
702 qreal startY = reverse ? toY + anchorPoint.y() : target->y() + anchorPoint.y();
703 qreal endX = reverse ? target->x() + anchorPoint.x() : toX + anchorPoint.x();
704 qreal endY = reverse ? target->y() + anchorPoint.y() : toY + anchorPoint.y();
706 prevBez.isValid = false;
707 painterPath = path->createPath(QPointF(startX, startY), QPointF(endX, endY), QStringList(), pathLength, attributePoints);
712 bool fixed = orientation == QQuickPathAnimation::Fixed;
713 QPointF currentPos = !painterPath.isEmpty() ? path->sequentialPointAt(painterPath, pathLength, attributePoints, prevBez, v, fixed ? 0 : &angle) : path->sequentialPointAt(v, fixed ? 0 : &angle);
715 //adjust position according to anchor point
716 if (!anchorPoint.isNull()) {
717 currentPos -= anchorPoint;
719 if (!anchorPoint.isNull() && !fixed)
720 target->setTransformOriginPoint(anchorPoint);
724 //### could cache properties rather than reconstructing each time
725 QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("x")), currentPos.x(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
726 QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("y")), currentPos.y(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
728 //adjust angle according to orientation
730 switch (orientation) {
731 case QQuickPathAnimation::RightFirst:
734 case QQuickPathAnimation::TopFirst:
737 case QQuickPathAnimation::LeftFirst:
738 angle = -angle + 180;
740 case QQuickPathAnimation::BottomFirst:
741 angle = -angle + 270;
748 if (atStart && !reverse) {
749 startRotation = target->rotation();
751 //shortest distance to correct orientation
752 qreal diff = angle - startRotation;
753 while (diff > 180.0) {
754 startRotation.value += 360.0;
757 while (diff < -180.0) {
758 startRotation.value -= 360.0;
763 //smoothly transition to the desired orientation
764 if (startRotation.isValid()) {
765 if (reverse && v == 0.0)
766 angle = startRotation;
767 else if (v < entryInterval)
768 angle = angle * v / entryInterval + startRotation * (entryInterval - v) / entryInterval;
770 if (endRotation.isValid()) {
771 qreal exitStart = 1 - exitInterval;
772 if (!reverse && v == 1.0)
774 else if (v > exitStart)
775 angle = endRotation * (v - exitStart) / exitInterval + angle * (exitInterval - (v - exitStart)) / exitInterval;
777 QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("rotation")), angle, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
781 NOTE: we don't always reset the transform origin, as it can cause a
782 visual jump if ending on an angle. This means that in some cases
783 (anchor point and orientation both specified, and ending at an angle)
784 the transform origin will always be set after running the path animation.
786 if ((reverse && v == 0.0) || (!reverse && v == 1.0)) {
787 if (!anchorPoint.isNull() && !fixed && qFuzzyIsNull(angle))
788 target->setTransformOriginPoint(QPointF());