slideshow/source/engine/animationfactory.cxx | 4 slideshow/source/engine/box2dtools.cxx | 181 +++++++++++++++++++++------ slideshow/source/inc/box2dtools.hxx | 33 ++++ 3 files changed, 175 insertions(+), 43 deletions(-)
New commits: commit b240db92100aec92e0e659c6b404ed1cd0ffcbc3 Author: Sarper Akdemir <q.sarperakde...@gmail.com> AuthorDate: Thu Aug 13 03:40:25 2020 +0300 Commit: Thorsten Behrens <thorsten.behr...@cib.de> CommitDate: Fri Aug 21 16:32:14 2020 +0200 make physics animations handle sequential animations correctly fixes a bug where if there is a sequence of animations between two physics animations that change the position, rotation or visibility of a shape - changes are not picked up by the latter physics animation. Change-Id: Ie8bb8b32588f4c7bf16317b5229adc5b0334d192 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/100715 Reviewed-by: Thorsten Behrens <thorsten.behr...@cib.de> Tested-by: Jenkins diff --git a/slideshow/source/engine/animationfactory.cxx b/slideshow/source/engine/animationfactory.cxx index efc4587356dd..8f6534a30539 100644 --- a/slideshow/source/engine/animationfactory.cxx +++ b/slideshow/source/engine/animationfactory.cxx @@ -323,7 +323,9 @@ namespace slideshow::internal { mpShapeManager->notifyShapeUpdate( mpShape ); if ( mpBox2DWorld->isInitialized() ) - mpBox2DWorld->queueDynamicPositionUpdate( mpShape->getXShape(), rOutPos ); + { + mpBox2DWorld->queueShapePathAnimationUpdate( mpShape->getXShape(), mpAttrLayer ); + } } return true; diff --git a/slideshow/source/engine/box2dtools.cxx b/slideshow/source/engine/box2dtools.cxx index f99e03bcd893..bdeabc12e985 100644 --- a/slideshow/source/engine/box2dtools.cxx +++ b/slideshow/source/engine/box2dtools.cxx @@ -206,6 +206,7 @@ box2DWorld::box2DWorld(const ::basegfx::B2DVector& rSlideSize) , mbHasWorldStepper(false) , mpXShapeToBodyMap() , maShapeParallelUpdateQueue() + , maShapeSequentialUpdate() { } @@ -255,6 +256,13 @@ void box2DWorld::createStaticFrameAroundSlide(const ::basegfx::B2DVector& rSlide pStaticBody->CreateFixture(&aFixtureDef); } +void box2DWorld::setShapePosition(const css::uno::Reference<com::sun::star::drawing::XShape> xShape, + const basegfx::B2DPoint& rOutPos) +{ + Box2DBodySharedPtr pBox2DBody = mpXShapeToBodyMap.find(xShape)->second; + pBox2DBody->setPosition(rOutPos); +} + void box2DWorld::setShapePositionByLinearVelocity( const css::uno::Reference<com::sun::star::drawing::XShape> xShape, const basegfx::B2DPoint& rOutPos, const double fPassedTime) @@ -276,6 +284,13 @@ void box2DWorld::setShapeLinearVelocity( pBox2DBody->setLinearVelocity(rVelocity); } +void box2DWorld::setShapeAngle(const css::uno::Reference<com::sun::star::drawing::XShape> xShape, + const double fAngle) +{ + Box2DBodySharedPtr pBox2DBody = mpXShapeToBodyMap.find(xShape)->second; + pBox2DBody->setAngle(fAngle); +} + void box2DWorld::setShapeAngleByAngularVelocity( const css::uno::Reference<com::sun::star::drawing::XShape> xShape, const double fAngle, const double fPassedTime) @@ -307,41 +322,75 @@ void box2DWorld::setShapeCollision( void box2DWorld::processUpdateQueue(const double fPassedTime) { - while (!maShapeParallelUpdateQueue.empty()) + if (maShapeSequentialUpdate.empty()) { - Box2DDynamicUpdateInformation& aQueueElement = maShapeParallelUpdateQueue.front(); - - if (aQueueElement.mnDelayForSteps > 0) + while (!maShapeParallelUpdateQueue.empty()) { - // it was queued as a delayed action, skip it, don't pop - aQueueElement.mnDelayForSteps--; + Box2DDynamicUpdateInformation& aQueueElement = maShapeParallelUpdateQueue.front(); + + if (aQueueElement.mnDelayForSteps > 0) + { + // it was queued as a delayed action, skip it, don't pop + aQueueElement.mnDelayForSteps--; + } + else + { + switch (aQueueElement.meUpdateType) + { + default: + case BOX2D_UPDATE_POSITION: + setShapePositionByLinearVelocity(aQueueElement.mxShape, + aQueueElement.maPosition, fPassedTime); + break; + case BOX2D_UPDATE_ANGLE: + setShapeAngleByAngularVelocity(aQueueElement.mxShape, aQueueElement.mfAngle, + fPassedTime); + break; + case BOX2D_UPDATE_SIZE: + break; + case BOX2D_UPDATE_VISIBILITY: + setShapeCollision(aQueueElement.mxShape, aQueueElement.mbVisibility); + break; + case BOX2D_UPDATE_LINEAR_VELOCITY: + setShapeLinearVelocity(aQueueElement.mxShape, aQueueElement.maVelocity); + break; + case BOX2D_UPDATE_ANGULAR_VELOCITY: + setShapeAngularVelocity(aQueueElement.mxShape, + aQueueElement.mfAngularVelocity); + } + maShapeParallelUpdateQueue.pop(); + } } - else + } + else + { + // clear the Parallel Update Queue since the updates in it + // are not relevant now - if there's any + maShapeParallelUpdateQueue = {}; + + for (auto& aIt : maShapeSequentialUpdate) { - switch (aQueueElement.meUpdateType) + const css::uno::Reference<css::drawing::XShape>& xShape = aIt.first.first; + const box2DNonsimulatedShapeUpdateType eUpdateType = aIt.first.second; + const Box2DStaticUpdateInformation& rUpdateInformation = aIt.second; + + switch (eUpdateType) { default: case BOX2D_UPDATE_POSITION: - setShapePositionByLinearVelocity(aQueueElement.mxShape, - aQueueElement.maPosition, fPassedTime); + setShapePosition(xShape, rUpdateInformation.maPosition); break; case BOX2D_UPDATE_ANGLE: - setShapeAngleByAngularVelocity(aQueueElement.mxShape, aQueueElement.mfAngle, - fPassedTime); - break; - case BOX2D_UPDATE_SIZE: + setShapeAngle(xShape, rUpdateInformation.mfAngle); break; case BOX2D_UPDATE_VISIBILITY: - setShapeCollision(aQueueElement.mxShape, aQueueElement.mbVisibility); - break; - case BOX2D_UPDATE_LINEAR_VELOCITY: - setShapeLinearVelocity(aQueueElement.mxShape, aQueueElement.maVelocity); + setShapeCollision(xShape, rUpdateInformation.mbVisibility); break; - case BOX2D_UPDATE_ANGULAR_VELOCITY: - setShapeAngularVelocity(aQueueElement.mxShape, aQueueElement.mfAngularVelocity); } - maShapeParallelUpdateQueue.pop(); } + + // After applying all required updates empty map + maShapeSequentialUpdate.clear(); } } @@ -389,9 +438,10 @@ void box2DWorld::queueDynamicPositionUpdate( void box2DWorld::queueLinearVelocityUpdate( const css::uno::Reference<com::sun::star::drawing::XShape>& xShape, - const basegfx::B2DVector& rVelocity) + const basegfx::B2DVector& rVelocity, const int nDelayForSteps) { - Box2DDynamicUpdateInformation aQueueElement = { xShape, {}, BOX2D_UPDATE_LINEAR_VELOCITY, 1 }; + Box2DDynamicUpdateInformation aQueueElement + = { xShape, {}, BOX2D_UPDATE_LINEAR_VELOCITY, nDelayForSteps }; aQueueElement.maVelocity = rVelocity; maShapeParallelUpdateQueue.push(aQueueElement); } @@ -406,9 +456,10 @@ void box2DWorld::queueDynamicRotationUpdate( void box2DWorld::queueAngularVelocityUpdate( const css::uno::Reference<com::sun::star::drawing::XShape>& xShape, - const double fAngularVelocity) + const double fAngularVelocity, const int nDelayForSteps) { - Box2DDynamicUpdateInformation aQueueElement = { xShape, {}, BOX2D_UPDATE_ANGULAR_VELOCITY, 1 }; + Box2DDynamicUpdateInformation aQueueElement + = { xShape, {}, BOX2D_UPDATE_ANGULAR_VELOCITY, nDelayForSteps }; aQueueElement.mfAngularVelocity = fAngularVelocity; maShapeParallelUpdateQueue.push(aQueueElement); } @@ -421,25 +472,64 @@ void box2DWorld::queueShapeVisibilityUpdate( maShapeParallelUpdateQueue.push(aQueueElement); } +void box2DWorld::queueShapePathAnimationUpdate( + const css::uno::Reference<com::sun::star::drawing::XShape>& xShape, + const slideshow::internal::ShapeAttributeLayerSharedPtr& pAttrLayer) +{ + // Workaround for PathAnimations since they do not have their own AttributeType + // - using PosX makes it register a DynamicPositionUpdate - + queueShapeAnimationUpdate(xShape, pAttrLayer, slideshow::internal::AttributeType::PosX); +} + void box2DWorld::queueShapeAnimationUpdate( const css::uno::Reference<com::sun::star::drawing::XShape>& xShape, const slideshow::internal::ShapeAttributeLayerSharedPtr& pAttrLayer, const slideshow::internal::AttributeType eAttrType) { - switch (eAttrType) + if (mbHasWorldStepper) // if there's a physics animation going on { - case slideshow::internal::AttributeType::Visibility: - queueShapeVisibilityUpdate(xShape, pAttrLayer->getVisibility()); - return; - case slideshow::internal::AttributeType::Rotate: - queueDynamicRotationUpdate(xShape, pAttrLayer->getRotationAngle()); - return; - case slideshow::internal::AttributeType::PosX: - case slideshow::internal::AttributeType::PosY: - queueDynamicPositionUpdate(xShape, { pAttrLayer->getPosX(), pAttrLayer->getPosY() }); - return; - default: - return; + switch (eAttrType) + { + case slideshow::internal::AttributeType::Visibility: + queueShapeVisibilityUpdate(xShape, pAttrLayer->getVisibility()); + return; + case slideshow::internal::AttributeType::Rotate: + queueDynamicRotationUpdate(xShape, pAttrLayer->getRotationAngle()); + return; + case slideshow::internal::AttributeType::PosX: + case slideshow::internal::AttributeType::PosY: + queueDynamicPositionUpdate(xShape, + { pAttrLayer->getPosX(), pAttrLayer->getPosY() }); + return; + default: + return; + } + } + else + { + Box2DStaticUpdateInformation aStaticUpdateInformation; + switch (eAttrType) + { + case slideshow::internal::AttributeType::Visibility: + aStaticUpdateInformation.mbVisibility = pAttrLayer->getVisibility(); + maShapeSequentialUpdate[std::make_pair(xShape, BOX2D_UPDATE_VISIBILITY)] + = aStaticUpdateInformation; + return; + case slideshow::internal::AttributeType::Rotate: + aStaticUpdateInformation.mfAngle = pAttrLayer->getRotationAngle(); + maShapeSequentialUpdate[std::make_pair(xShape, BOX2D_UPDATE_ANGLE)] + = aStaticUpdateInformation; + return; + case slideshow::internal::AttributeType::PosX: + case slideshow::internal::AttributeType::PosY: + aStaticUpdateInformation.maPosition + = basegfx::B2DPoint(pAttrLayer->getPosX(), pAttrLayer->getPosY()); + maShapeSequentialUpdate[std::make_pair(xShape, BOX2D_UPDATE_POSITION)] + = aStaticUpdateInformation; + return; + default: + return; + } } } @@ -450,11 +540,11 @@ void box2DWorld::queueShapeAnimationEndUpdate( switch (eAttrType) { case slideshow::internal::AttributeType::Rotate: - queueAngularVelocityUpdate(xShape, 0.0f); + queueAngularVelocityUpdate(xShape, 0.0, 1); return; case slideshow::internal::AttributeType::PosX: case slideshow::internal::AttributeType::PosY: - queueLinearVelocityUpdate(xShape, { 0, 0 }); + queueLinearVelocityUpdate(xShape, { 0, 0 }, 1); return; default: return; @@ -617,6 +707,12 @@ box2DBody::box2DBody(std::shared_ptr<b2Body> pBox2DBody, double fScaleFactor) return ::basegfx::B2DPoint(fX, fY); } +void box2DBody::setPosition(const basegfx::B2DPoint& rPos) +{ + mpBox2DBody->SetTransform(convertB2DPointToBox2DVec2(rPos, mfScaleFactor), + mpBox2DBody->GetAngle()); +} + void box2DBody::setPositionByLinearVelocity(const basegfx::B2DPoint& rDesiredPos, const double fPassedTime) { @@ -675,6 +771,11 @@ double box2DBody::getAngle() return ::basegfx::rad2deg(-fAngle); } +void box2DBody::setAngle(const double fAngle) +{ + mpBox2DBody->SetTransform(mpBox2DBody->GetPosition(), ::basegfx::deg2rad(-fAngle)); +} + void box2DBody::setType(box2DBodyType eType) { mpBox2DBody->SetType(getBox2DInternalBodyType(eType)); diff --git a/slideshow/source/inc/box2dtools.hxx b/slideshow/source/inc/box2dtools.hxx index c4113b931173..08b0f4b557cf 100644 --- a/slideshow/source/inc/box2dtools.hxx +++ b/slideshow/source/inc/box2dtools.hxx @@ -12,6 +12,7 @@ #include "shape.hxx" #include "shapeattributelayer.hxx" #include "attributemap.hxx" +#include <map> #include <unordered_map> #include <queue> @@ -64,6 +65,13 @@ struct Box2DDynamicUpdateInformation int mnDelayForSteps = 0; }; +union Box2DStaticUpdateInformation { + ::basegfx::B2DPoint maPosition; + bool mbVisibility; + double mfAngle; + Box2DStaticUpdateInformation() {} +}; + /** Class that manages the Box2D World This class is used when there's a simulated animation going on, @@ -78,15 +86,25 @@ private: /// Scale factor for conversions between LO user space coordinates to Box2D World coordinates double mfScaleFactor; bool mbShapesInitialized; + /// Holds whether or not there is a Physics Animation node that + /// is stepping the Box2D World bool mbHasWorldStepper; std::unordered_map<css::uno::Reference<css::drawing::XShape>, Box2DBodySharedPtr> mpXShapeToBodyMap; /// Holds any information needed to keep LO animations and Box2D world in sync + /// if they are going in parallel std::queue<Box2DDynamicUpdateInformation> maShapeParallelUpdateQueue; + /// Holds necessary information to update a shape's body that was altered by an + /// animation effect while there was no Physics Animation going in parallel + std::map<std::pair<css::uno::Reference<css::drawing::XShape>, box2DNonsimulatedShapeUpdateType>, + Box2DStaticUpdateInformation> + maShapeSequentialUpdate; /// Creates a static frame in Box2D world that corresponds to the slide borders void createStaticFrameAroundSlide(const ::basegfx::B2DVector& rSlideSize); + void setShapePosition(const css::uno::Reference<css::drawing::XShape> xShape, + const ::basegfx::B2DPoint& rOutPos); /** Sets shape's corresponding Box2D body to specified position Sets shape's corresponding Box2D body to specified position as if @@ -108,6 +126,8 @@ private: void setShapeLinearVelocity(const css::uno::Reference<com::sun::star::drawing::XShape> xShape, const basegfx::B2DVector& rVelocity); + void setShapeAngle(const css::uno::Reference<com::sun::star::drawing::XShape> xShape, + const double fAngle); /** Sets shape's corresponding Box2D body to specified angle Sets shape's corresponding Box2D body to specified angle as if @@ -165,7 +185,7 @@ private: /// to take place after the next step of the box2DWorld void queueAngularVelocityUpdate(const css::uno::Reference<com::sun::star::drawing::XShape>& xShape, - const double fAngularVelocity); + const double fAngularVelocity, const int nDelayForSteps = 0); /// Queue an update that changes collision of the corresponding body /// on the next step of the box2DWorld, used for animations that change visibility @@ -233,13 +253,18 @@ public: /// Queue a linear velocity update for the corresponding body /// to take place after the next step of the box2DWorld void queueLinearVelocityUpdate(const css::uno::Reference<css::drawing::XShape>& xShape, - const ::basegfx::B2DVector& rVelocity); + const ::basegfx::B2DVector& rVelocity, + const int nDelayForSteps = 0); void queueShapeAnimationUpdate(const css::uno::Reference<css::drawing::XShape>& xShape, const slideshow::internal::ShapeAttributeLayerSharedPtr& pAttrLayer, const slideshow::internal::AttributeType eAttrType); + void queueShapePathAnimationUpdate( + const css::uno::Reference<com::sun::star::drawing::XShape>& xShape, + const slideshow::internal::ShapeAttributeLayerSharedPtr& pAttrLayer); + void queueShapeAnimationEndUpdate(const css::uno::Reference<css::drawing::XShape>& xShape, const slideshow::internal::AttributeType eAttrType); }; @@ -259,6 +284,8 @@ public: /// @return current position in LO user space coordinates ::basegfx::B2DPoint getPosition(); + void setPosition(const ::basegfx::B2DPoint& rPos); + /** Sets body to specified position Sets body to specified position as if the body had @@ -298,6 +325,8 @@ public: /// @return current angle of rotation of the body double getAngle(); + void setAngle(const double fAngle); + /// Set type of the body void setType(box2DBodyType eType); _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits