diff options
author | Sarper Akdemir <q.sarperakdemir@gmail.com> | 2020-08-27 17:08:38 +0300 |
---|---|---|
committer | Thorsten Behrens <Thorsten.Behrens@CIB.de> | 2020-08-31 12:25:59 +0200 |
commit | 8e36582d9289c3774f2733b021fafc93370cffae (patch) | |
tree | b007d4944f5ed342b1051e4a8785cd359d843236 /slideshow | |
parent | 55dda1367a845b37184cedc287e5f8f03ef0f6cf (diff) |
document physics animations and related code
Change-Id: I1369588dd757d5fedcd7e91eabe0020e5cf60c56
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/101492
Tested-by: Jenkins
Reviewed-by: Thorsten Behrens <Thorsten.Behrens@CIB.de>
Diffstat (limited to 'slideshow')
-rw-r--r-- | slideshow/README | 33 | ||||
-rw-r--r-- | slideshow/source/engine/animationfactory.cxx | 7 | ||||
-rw-r--r-- | slideshow/source/engine/box2dtools.cxx | 56 | ||||
-rw-r--r-- | slideshow/source/inc/box2dtools.hxx | 204 |
4 files changed, 251 insertions, 49 deletions
diff --git a/slideshow/README b/slideshow/README index e523458531ae..e67379f43c89 100644 --- a/slideshow/README +++ b/slideshow/README @@ -8,3 +8,36 @@ written by GSOC student Shane.M.Mathews. Radek has later polished the code a bit, added few new 3D transitions, added infrastructure for vertex and fragment shaders. Wrote few transitions with fragment shader too. + +== Physics Animation Effects == + +Physics animation effects are simulated by external 2d physics engine +library Box2D. They don't directly call Box2D functions but instead +use the wrapper in: +* slideshow/source/inc/box2dtools.hxx +* slideshow/source/engine/box2dtools.cxx + +The wrapper has two corresponding classes to manage the Box2D world +and Box2D bodies. + +When a physics animation starts, a Box2DWorld is initiated and +populated with every shape that is part of the foreground (which are +shapes that do not belong to the master slide and not a background +shape). + +After creation until the end of the slide (not the whole slideshow) +the Box2D World isn't destroyed and reused. But the bodies that +represent the shapes in the slide get destroyed when there's a point +in time that there's no physics animation in progress. And recreated +when another physics animation starts. + +If there are multiple physics animations in parallel only one of them +takes the role of stepping through the simulation. + +If there are other animation effects that go in parallel which change +the shape position, rotation, or visibility - they also report the +change to Box2D World. These updates are collected in a queue in +Box2DWorld and processed before stepping through the simulation. +To achieve convincing results these updates are performed by setting +the box2d body's linear velocity or angular velocity instead of +setting directly it's position or rotation. diff --git a/slideshow/source/engine/animationfactory.cxx b/slideshow/source/engine/animationfactory.cxx index db141c906a50..e874b0924cd8 100644 --- a/slideshow/source/engine/animationfactory.cxx +++ b/slideshow/source/engine/animationfactory.cxx @@ -288,7 +288,8 @@ namespace slideshow::internal if( mpShape->isContentChanged() ) mpShapeManager->notifyShapeUpdate( mpShape ); - // since animation ended zero out the linear velocity + // if there is a physics animation going on report the animation ending + // and zero out the velocity of the shape if( mpBox2DWorld->isInitialized() ) mpBox2DWorld->queueLinearVelocityUpdate( mpShape->getXShape(), {0,0}); } @@ -323,6 +324,8 @@ namespace slideshow::internal if( mpShape->isContentChanged() ) { mpShapeManager->notifyShapeUpdate( mpShape ); + + // if there's a physics animation going on report the change to it if ( mpBox2DWorld->isInitialized() ) { mpBox2DWorld->queueShapePathAnimationUpdate( mpShape->getXShape(), @@ -654,6 +657,7 @@ namespace slideshow::internal if( mpBox2DWorld && mpBox2DWorld->isInitialized() ) { + // if there's a physics animation going on report the animation ending to it mpBox2DWorld->queueShapeAnimationEndUpdate( mpShape->getXShape(), meAttrType ); } @@ -717,6 +721,7 @@ namespace slideshow::internal if( mpBox2DWorld && mpBox2DWorld->isInitialized() ) { + // if there's a physics animation going on report the change to it mpBox2DWorld->queueShapeAnimationUpdate( mpShape->getXShape(), mpAttrLayer, meAttrType, mbAnimationFirstUpdate ); } diff --git a/slideshow/source/engine/box2dtools.cxx b/slideshow/source/engine/box2dtools.cxx index 5cefff743b2e..3c7c3deb657c 100644 --- a/slideshow/source/engine/box2dtools.cxx +++ b/slideshow/source/engine/box2dtools.cxx @@ -35,6 +35,8 @@ double calculateScaleFactor(const ::basegfx::B2DVector& rSlideSize) double fWidth = rSlideSize.getX(); double fHeight = rSlideSize.getY(); + // Scale factor is based on whatever is the larger + // value between slide width and height if (fWidth > fHeight) return BOX2D_SLIDE_SIZE_IN_METERS / fWidth; else @@ -90,6 +92,8 @@ void addTriangleVectorToBody(const basegfx::triangulator::B2DTriangleVector& rTr convertB2DPointToBox2DVec2(aTriangle.getC(), fScaleFactor) }; bool bValidPointDistance = true; + + // check whether the triangle has degenerately close points for (int nPointIndexA = 0; nPointIndexA < 3; nPointIndexA++) { for (int nPointIndexB = 0; nPointIndexB < 3; nPointIndexB++) @@ -97,7 +101,6 @@ void addTriangleVectorToBody(const basegfx::triangulator::B2DTriangleVector& rTr if (nPointIndexA == nPointIndexB) continue; - // check whether the triangle would be a degenerately small one if (b2DistanceSquared(aTriangleVertices[nPointIndexA], aTriangleVertices[nPointIndexB]) < 0.003f) @@ -106,8 +109,10 @@ void addTriangleVectorToBody(const basegfx::triangulator::B2DTriangleVector& rTr } } } + if (bValidPointDistance) { + // create a fixture that represents the triangle aPolygonShape.Set(aTriangleVertices, 3); aFixture.shape = &aPolygonShape; aFixture.density = fDensity; @@ -141,6 +146,7 @@ void addEdgeShapeToBody(const basegfx::B2DPolygon& rPolygon, b2Body* aBody, cons basegfx::B2DPoint aPointB; if (nIndex != 0) { + // get two adjacent points to create an edge out of aPointA = aPolygon.getB2DPoint(nIndex - 1); aPointB = aPolygon.getB2DPoint(nIndex); } @@ -155,14 +161,22 @@ void addEdgeShapeToBody(const basegfx::B2DPolygon& rPolygon, b2Body* aBody, cons continue; } + // create a vector that represents the direction of the edge + // and make it a unit vector b2Vec2 aEdgeUnitVec(convertB2DPointToBox2DVec2(aPointB, fScaleFactor) - convertB2DPointToBox2DVec2(aPointA, fScaleFactor)); aEdgeUnitVec.Normalize(); + // create a unit vector that represents Normal of the edge b2Vec2 aEdgeNormal(-aEdgeUnitVec.y, aEdgeUnitVec.x); + // if there was an edge previously created it should just connect + // using it's ending points so that there are no empty spots + // between edge segments, if not use wherever aPointA is at if (!bHasPreviousQuadrilateralEdge) { + // the point is translated along the edge normal both directions by + // fHalfWidth to create a quadrilateral edge aQuadrilateralVertices[0] = convertB2DPointToBox2DVec2(aPointA, fScaleFactor) + fHalfWidth * aEdgeNormal; aQuadrilateralVertices[1] @@ -174,11 +188,13 @@ void addEdgeShapeToBody(const basegfx::B2DPolygon& rPolygon, b2Body* aBody, cons aQuadrilateralVertices[3] = convertB2DPointToBox2DVec2(aPointB, fScaleFactor) + -fHalfWidth * aEdgeNormal; + // check whether the edge would have degenerately close points bool bValidPointDistance = b2DistanceSquared(aQuadrilateralVertices[0], aQuadrilateralVertices[2]) > 0.003f; if (bValidPointDistance) { + // create a quadrilateral shaped fixture to represent the edge aPolygonShape.Set(aQuadrilateralVertices, 4); aFixture.shape = &aPolygonShape; aFixture.density = fDensity; @@ -243,7 +259,8 @@ void box2DWorld::createStaticFrameAroundSlide(const ::basegfx::B2DVector& rSlide aBodyDef.type = b2_staticBody; aBodyDef.position.Set(0, 0); - // not going to be stored anywhere, Box2DWorld will handle this body + // not going to be stored anywhere, will live + // as long as the Box2DWorld does b2Body* pStaticBody = mpBox2DWorld->CreateBody(&aBodyDef); // create an edge loop that represents slide frame @@ -256,6 +273,7 @@ void box2DWorld::createStaticFrameAroundSlide(const ::basegfx::B2DVector& rSlide b2ChainShape aEdgesChainShape; aEdgesChainShape.CreateLoop(aEdgePoints, 4); + // create the fixture for the shape b2FixtureDef aFixtureDef; aFixtureDef.shape = &aEdgesChainShape; pStaticBody->CreateFixture(&aFixtureDef); @@ -379,6 +397,8 @@ void box2DWorld::initateAllShapesAsStaticBodies( std::unordered_map<css::uno::Reference<css::drawing::XShape>, bool> aXShapeBelongsToAGroup; // iterate over the shapes in the current slide and flag them if they belong to a group + // will flag the only ones that are belong to a group since std::unordered_map operator[] + // defaults the value to false if the key doesn't have a corresponding value for (auto aIt = aXShapeToShapeMap.begin(); aIt != aXShapeToShapeMap.end(); aIt++) { slideshow::internal::ShapeSharedPtr pShape = aIt->second; @@ -387,6 +407,7 @@ void box2DWorld::initateAllShapesAsStaticBodies( SdrObject* pTemp = SdrObject::getSdrObjectFromXShape(pShape->getXShape()); if (pTemp && pTemp->IsGroupObject()) { + // if it is a group object iterate over it's childs and flag them SdrObjList* aObjList = pTemp->GetSubList(); const size_t nObjCount(aObjList->GetObjCount()); @@ -404,6 +425,9 @@ void box2DWorld::initateAllShapesAsStaticBodies( for (auto aIt = aXShapeToShapeMap.begin(); aIt != aXShapeToShapeMap.end(); aIt++) { slideshow::internal::ShapeSharedPtr pShape = aIt->second; + // only create static bodies for the shapes that do not belong to a group + // groups themselves will have one body that represents the whole shape + // collection if (pShape->isForeground() && !aXShapeBelongsToAGroup[pShape->getXShape()]) { Box2DBodySharedPtr pBox2DBody = createStaticBody(pShape); @@ -411,7 +435,7 @@ void box2DWorld::initateAllShapesAsStaticBodies( mpXShapeToBodyMap.insert(std::make_pair(pShape->getXShape(), pBox2DBody)); if (!pShape->isVisible()) { - // if the shape isn't visible, mark it + // if the shape isn't visible, queue an update for it queueShapeVisibilityUpdate(pShape->getXShape(), false); } } @@ -504,7 +528,7 @@ void box2DWorld::queueShapeAnimationUpdate( return; case slideshow::internal::AttributeType::PosX: case slideshow::internal::AttributeType::PosY: - if (bIsFirstUpdate) + if (bIsFirstUpdate) // if it is the first update shape should _teleport_ to the position queueShapePositionUpdate(xShape, { pAttrLayer->getPosX(), pAttrLayer->getPosY() }); else queueDynamicPositionUpdate(xShape, @@ -521,6 +545,8 @@ void box2DWorld::queueShapeAnimationEndUpdate( { switch (eAttrType) { + // end updates that change the velocity are delayed for a step + // since we do not want them to override the last position/angle case slideshow::internal::AttributeType::Rotate: queueAngularVelocityUpdate(xShape, 0.0, 1); return; @@ -536,12 +562,16 @@ void box2DWorld::queueShapeAnimationEndUpdate( void box2DWorld::alertPhysicsAnimationEnd(const slideshow::internal::ShapeSharedPtr& pShape) { Box2DBodySharedPtr pBox2DBody = mpXShapeToBodyMap.find(pShape->getXShape())->second; + // since the animation ended make the body static makeBodyStatic(pBox2DBody); pBox2DBody->setRestitution(fDefaultStaticBodyBounciness); if (--mnPhysicsAnimationCounter == 0) { + // if there are no more physics animation effects going on clean up maShapeParallelUpdateQueue = {}; mbShapesInitialized = false; + // clearing the map will make the box2d bodies get + // destroyed if there's nothing else that owns them mpXShapeToBodyMap.clear(); } } @@ -572,8 +602,11 @@ double box2DWorld::stepAmount(const double fPassedTime, const float fTimeStep, assert(mpBox2DWorld); unsigned int nStepAmount = static_cast<unsigned int>(std::round(fPassedTime / fTimeStep)); + // find the actual time that will be stepped through so + // that the updates can be processed using that value double fTimeSteppedThrough = fTimeStep * nStepAmount; + // do the updates required to simulate other animaton effects going in parallel processUpdateQueue(fTimeSteppedThrough); for (unsigned int nStepCounter = 0; nStepCounter < nStepAmount; nStepCounter++) @@ -647,9 +680,11 @@ Box2DBodySharedPtr box2DWorld::createStaticBody(const slideshow::internal::Shape ->getTopmostAttributeLayer(); if (pShapeAttributeLayer && pShapeAttributeLayer->isRotationAngleValid()) { + // if the shape's rotation value was altered by another animation effect set it. aBodyDef.angle = ::basegfx::deg2rad(-pShapeAttributeLayer->getRotationAngle()); } + // create a shared pointer with a destructor so that the body will be properly destroyed std::shared_ptr<b2Body> pBody(mpBox2DWorld->CreateBody(&aBodyDef), [](b2Body* pB2Body) { pB2Body->GetWorld()->DestroyBody(pB2Body); }); @@ -679,6 +714,7 @@ Box2DBodySharedPtr box2DWorld::createStaticBody(const slideshow::internal::Shape aPolyPolygon.removeDoublePoints(); // make polygon coordinates relative to the center of the shape instead of top left of the slide + // since box2d shapes are expressed this way aPolyPolygon = basegfx::utils::distort(aPolyPolygon, aPolyPolygon.getB2DRange(), { -aShapeBounds.getWidth() / 2, -aShapeBounds.getHeight() / 2 }, @@ -689,8 +725,10 @@ Box2DBodySharedPtr box2DWorld::createStaticBody(const slideshow::internal::Shape if (pSdrObject->IsClosedObj() && !pSdrObject->IsEdgeObj() && pSdrObject->HasFillStyle()) { basegfx::triangulator::B2DTriangleVector aTriangleVector; + // iterate over the polygons of the shape and create representations for them for (auto& rPolygon : aPolyPolygon) { + // if the polygon is closed it will be represented by triangles if (rPolygon.isClosed()) { basegfx::triangulator::B2DTriangleVector aTempTriangleVector( @@ -698,7 +736,7 @@ Box2DBodySharedPtr box2DWorld::createStaticBody(const slideshow::internal::Shape aTriangleVector.insert(aTriangleVector.end(), aTempTriangleVector.begin(), aTempTriangleVector.end()); } - else + else // otherwise it will be an edge representation (example: smile line of the smiley shape) { addEdgeShapeToBody(rPolygon, pBody.get(), fDensity, fFriction, static_cast<float>(fDefaultStaticBodyBounciness), mfScaleFactor); @@ -739,10 +777,12 @@ void box2DBody::setPosition(const basegfx::B2DPoint& rPos) void box2DBody::setPositionByLinearVelocity(const basegfx::B2DPoint& rDesiredPos, const double fPassedTime) { + // kinematic bodies are not affected by other bodies, but unlike static ones can still have velocity if (mpBox2DBody->GetType() != b2_kinematicBody) mpBox2DBody->SetType(b2_kinematicBody); ::basegfx::B2DPoint aCurrentPos = getPosition(); + // calculate the velocity needed to reach the rDesiredPos in the given time frame ::basegfx::B2DVector aVelocity = (rDesiredPos - aCurrentPos) / fPassedTime; setLinearVelocity(aVelocity); @@ -750,6 +790,7 @@ void box2DBody::setPositionByLinearVelocity(const basegfx::B2DPoint& rDesiredPos void box2DBody::setAngleByAngularVelocity(const double fDesiredAngle, const double fPassedTime) { + // kinematic bodies are not affected by other bodies, but unlike static ones can still have velocity if (mpBox2DBody->GetType() != b2_kinematicBody) mpBox2DBody->SetType(b2_kinematicBody); @@ -779,10 +820,13 @@ void box2DBody::setAngularVelocity(const double fAngularVelocity) void box2DBody::setCollision(const bool bCanCollide) { + // collision have to be set for each fixture of the body individually for (b2Fixture* pFixture = mpBox2DBody->GetFixtureList(); pFixture; pFixture = pFixture->GetNext()) { b2Filter aFilter = pFixture->GetFilterData(); + // 0xFFFF means collides with everything + // 0x0000 means collides with nothing aFilter.maskBits = bCanCollide ? 0xFFFF : 0x0000; pFixture->SetFilterData(aFilter); } @@ -801,12 +845,14 @@ void box2DBody::setAngle(const double fAngle) void box2DBody::setDensityAndRestitution(const double fDensity, const double fRestitution) { + // density and restitution have to be set for each fixture of the body individually for (b2Fixture* pFixture = mpBox2DBody->GetFixtureList(); pFixture; pFixture = pFixture->GetNext()) { pFixture->SetDensity(static_cast<float>(fDensity)); pFixture->SetRestitution(static_cast<float>(fRestitution)); } + // without resetting the massdata of the body, density change won't take effect mpBox2DBody->ResetMassData(); } diff --git a/slideshow/source/inc/box2dtools.hxx b/slideshow/source/inc/box2dtools.hxx index ae9daa5fee35..a71af1d34bef 100644 --- a/slideshow/source/inc/box2dtools.hxx +++ b/slideshow/source/inc/box2dtools.hxx @@ -54,6 +54,7 @@ enum box2DNonsimulatedShapeUpdateType /// body of a shape that was altered by an animation effect struct Box2DDynamicUpdateInformation { + /// reference to the shape that the update belongs to css::uno::Reference<css::drawing::XShape> mxShape; union { ::basegfx::B2DPoint maPosition; @@ -63,42 +64,60 @@ struct Box2DDynamicUpdateInformation bool mbVisibility; }; box2DNonsimulatedShapeUpdateType meUpdateType; + /// amount of steps to delay the update for int mnDelayForSteps = 0; }; /** Class that manages the Box2D World - This class is used when there's a simulated animation going on, - it handles the stepping through the simulated world, updating the - shapes in the simulated world if they were changed by ongoing animations. + This class is used when there's a physics animation going on, + it handles the stepping through the box2d world, updating the + shapes in the box2d world if they were changed by ongoing animations. */ class box2DWorld { private: - /// Pointer to the real Box2D World that this class manages for simulations + /// Pointer to the real Box2D World that this class manages std::unique_ptr<b2World> mpBox2DWorld; /// 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 + /// Holds whether or not there is a PhysicsAnimation that + /// is stepping the Box2D World. Used to create a lock mechanism bool mbHasWorldStepper; + /// Number of Physics Animations going on int mnPhysicsAnimationCounter; 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 + + /** Queue that holds any required information to keep LO animation effects + and Box2DWorld in sync + + Is processed before every step of the box2d world by processUpdateQueue. + Holds position, rotation, visibility etc. changes and associated values. + */ std::queue<Box2DDynamicUpdateInformation> maShapeParallelUpdateQueue; /// Creates a static frame in Box2D world that corresponds to the slide borders void createStaticFrameAroundSlide(const ::basegfx::B2DVector& rSlideSize); + /** Sets shape's corresponding Box2D body to the specified position + + Body is teleported to the specified position, not moved + + @param xShape + Shape reference + + @param rOutPos + Position in LO user space coordinates + */ 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 - the body had velocity to reach that point in given time frame + /** Moves shape's corresponding Box2D body to specified position + + Moves shape's corresponding Box2D body to specified position as if + the body had velocity to reach that point in given time frame. @param xShape Shape reference @@ -112,35 +131,63 @@ private: void setShapePositionByLinearVelocity(const css::uno::Reference<css::drawing::XShape> xShape, const ::basegfx::B2DPoint& rOutPos, const double fPassedTime); - /// Sets linear velocity of the shape's corresponding body in the Box2D world + + /** Sets linear velocity of the shape's corresponding Box2D body + + Moves shape's corresponding Box2D body to specified position as if + the body had velocity to reach that point in given time frame. + + @param xShape + Shape reference + + @param rVelocity + Velocity vector in LO user space coordinates. + */ void setShapeLinearVelocity(const css::uno::Reference<com::sun::star::drawing::XShape> xShape, const basegfx::B2DVector& rVelocity); + /** Sets rotation angle of the shape's corresponding Box2D body + + @param xShape + Shape reference + + @param fAngle + Angle of rotation in degrees. + */ 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 - the body had angular velocity to reach that point in given time frame + /** Rotates shape's corresponding Box2D body to specified angle + + Rotates the Box2D body to specified angle as if the body + had exact angular velocity to reach that point in given + time frame @param xShape Shape reference @param fAngle - Position in LO user space coordinates + Angle of rotation in degrees. @param fPassedTime - Time frame which the Box2D body should move to the specified position. + Time frame which the Box2D body should rotate to the specified angle. */ void setShapeAngleByAngularVelocity( const css::uno::Reference<com::sun::star::drawing::XShape> xShape, const double fAngle, const double fPassedTime); - /// Sets angular velocity of the shape's corresponding body in the Box2D world + /** Sets angular velocity of the shape's corresponding Box2D body. + + @param xShape + Shape reference + + @param fAngularVelocity + Angular velocity in degrees per second. + */ void setShapeAngularVelocity(const css::uno::Reference<com::sun::star::drawing::XShape> xShape, const double fAngularVelocity); - /** Set whether a shape can have collision in the Box2D World + /** Sets whether a shape's corresponding Box2D body has collision in the Box2D World or not Used for animations that change the visibility of the shape. @@ -153,7 +200,8 @@ private: */ void setShapeCollision(const css::uno::Reference<com::sun::star::drawing::XShape> xShape, const bool bCanCollide); - /** Process the updates queued in the maShapeUpdateQueue + + /** Process the updates queued in the maShapeParallelUpdateQueue Called on each step of the box2DWorld. @@ -162,23 +210,26 @@ private: */ void processUpdateQueue(const double fPassedTime); - /// Simulate and step through time in the Box2D World + /** Simulate and step through time in the Box2D World + + Used in stepAmount + + @attention fTimeStep should not vary. + */ void step(const float fTimeStep = 1.0f / 100.0f, const int nVelocityIterations = 6, const int nPositionIterations = 2); - /// Queue a rotation update on the next step of the box2DWorld for the corresponding body + /// Queue a rotation update that is simulated as if shape's corresponding box2D body rotated to given angle when processed void queueDynamicRotationUpdate(const css::uno::Reference<com::sun::star::drawing::XShape>& xShape, const double fAngle); - /// Queue an angular velocity update for the corresponding body - /// to take place after the next step of the box2DWorld + /// Queue an angular velocity update that sets the shape's corresponding box2D body angular velocity to the given value when processed void queueAngularVelocityUpdate(const css::uno::Reference<com::sun::star::drawing::XShape>& xShape, 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 + /// Queue an collision update that sets the collision of shape's corresponding box2D body when processed void queueShapeVisibilityUpdate(const css::uno::Reference<css::drawing::XShape>& xShape, const bool bVisibility); @@ -198,21 +249,35 @@ public: @return Amount of time actually stepped through, since it is possible to only step through a multiple of fTimeStep + + @attention fTimeStep should not vary. */ double stepAmount(const double fPassedTime, const float fTimeStep = 1.0f / 100.0f, const int nVelocityIterations = 6, const int nPositionIterations = 2); /// @return whether shapes in the slide are initialized as Box2D bodies or not bool shapesInitialized(); - /// @return whether the Box2D shape is initialized or not + /// @return whether the Box2D World is initialized or not bool isInitialized(); - /** Make the Box2D body corresponding to the given shape a dynamic one + /** Make the shape's corresponding box2D body a dynamic one. A dynamic body will be affected by other bodies and the gravity. - @param pShape - Pointer to the shape to alter the corresponding Box2D body of + @param xShape + Shape reference + + @param rStartVelocity + Velocity of the shape after making it dynamic + + @param fDensity + Density of the body that is in kg/m^2 + + @param fBounciness + Bounciness of the body that is usually in between [0,1]. + Even though it could take values that are >1, it is way too chaotic. + + @return box2d body pointer */ Box2DBodySharedPtr makeShapeDynamic(const css::uno::Reference<css::drawing::XShape>& xShape, const basegfx::B2DVector& rStartVelocity, @@ -220,14 +285,21 @@ public: /** Make the Box2D body corresponding to the given shape a static one - A static body will not be affected by other bodies and the gravity. + A static body will not be affected by other bodies and the gravity. But will + affect other bodies that are dynamic (will still collide with them but won't + move etc.) @param pShape Pointer to the shape to alter the corresponding Box2D body of + + @return box2d body pointer */ Box2DBodySharedPtr makeShapeStatic(const slideshow::internal::ShapeSharedPtr& pShape); - /// Create a static body from the given shape's geometry + /** Create a static body that represeted by the shape's geometry + + @return pointer to the box2d body + */ Box2DBodySharedPtr createStaticBody(const slideshow::internal::ShapeSharedPtr& rShape, const float fDensity = 1.0f, const float fFriction = 0.3f); @@ -241,32 +313,47 @@ public: /// Set the flag for whether the box2DWorld has a stepper or not void setHasWorldStepper(const bool bHasWorldStepper); - /// Queue a position update the next step of the box2DWorld for the corresponding body + /// Queue a position update that is simulated as if shape's corresponding box2D body moved to given position when processed void queueDynamicPositionUpdate(const css::uno::Reference<css::drawing::XShape>& xShape, const ::basegfx::B2DPoint& rOutPos); - /// Queue a linear velocity update for the corresponding body - /// to take place after the next step of the box2DWorld + /// Queue a update that sets the corresponding box2D body's linear velocity to the given value when processed void queueLinearVelocityUpdate(const css::uno::Reference<css::drawing::XShape>& xShape, const ::basegfx::B2DVector& rVelocity, const int nDelayForSteps = 0); + /// Queue an appropraite update for the animation effect that is in parallel with a physics animation void queueShapeAnimationUpdate(const css::uno::Reference<css::drawing::XShape>& xShape, const slideshow::internal::ShapeAttributeLayerSharedPtr& pAttrLayer, const slideshow::internal::AttributeType eAttrType, const bool bIsFirstUpdate); + /// Queue an appropraite update for a path animation that is in parallel with a physics animation void queueShapePathAnimationUpdate( const css::uno::Reference<com::sun::star::drawing::XShape>& xShape, const slideshow::internal::ShapeAttributeLayerSharedPtr& pAttrLayer, const bool bIsFirstUpdate); + /// Queue an appropraite update for the animation effect that just ended void queueShapeAnimationEndUpdate(const css::uno::Reference<css::drawing::XShape>& xShape, const slideshow::internal::AttributeType eAttrType); + /** Alert that a physics animation effect has ended + + Makes the given shape static, if this was the last physics animation effect + that was in parallel, box2d bodies that are owned by the mpXShapeToBodyMap + are dumped and potentially destroyed. + + @attention the box2d body owned by the PhysicsAnimation won't be destroyed. + */ void alertPhysicsAnimationEnd(const slideshow::internal::ShapeSharedPtr& pShape); + /** Alert that a physics animation effect has started + + Initiates the box2D world if it is not initiated yet, and likewise constructs + box2d bodies for the shapes in the current slide if they are not constructed. + */ void alertPhysicsAnimationStart(const ::basegfx::B2DVector& rSlideSize, const slideshow::internal::ShapeManagerSharedPtr& pShapeManager); @@ -287,12 +374,17 @@ public: /// @return current position in LO user space coordinates ::basegfx::B2DPoint getPosition(); + /** Set the position of box2d body + + @param rPos + Position in LO user space coordinates + */ void setPosition(const ::basegfx::B2DPoint& rPos); - /** Sets body to specified position + /** Moves body to the specified position - Sets body to specified position as if the body had - velocity to reach that point in given time frame + Moves body to the specified position by setting velocity of + the body so that it reaches to rDesiredPos in given time fram @param rDesiredPos Position to arrive in the time frame @@ -303,23 +395,31 @@ public: void setPositionByLinearVelocity(const ::basegfx::B2DPoint& rDesiredPos, const double fPassedTime); - /// Sets linear velocity of the body + /** Sets linear velocity of the body + + @param rVelocity + Velocity vector in LO user space coordinates + */ void setLinearVelocity(const ::basegfx::B2DVector& rVelocity); - /** Sets body to specified angle of rotation + /** Rotate body to specified angle of rotation - Sets body to specified rotation as if the body had + Rotates body to specified rotation as if the body had angular velocity to reach that state in given time frame @param fDesiredAngle - Rotation angle to arrive in the time frame + Rotation angle in degrees to arrive in the time frame @param fPassedTime Amount of time for the movement to take place */ void setAngleByAngularVelocity(const double fDesiredAngle, const double fPassedTime); - /// Sets angular velocity of the body + /** Sets angular velocity of the body + + @param fAngularVelocity + Angular velocity in degrees per second + */ void setAngularVelocity(const double fAngularVelocity); /// Sets whether the body have collisions or not @@ -328,10 +428,28 @@ public: /// @return current angle of rotation of the body double getAngle(); + /** Set angle of the box2d body + + @param fAngle + Angle in degrees + */ void setAngle(const double fAngle); + /** Set density and restitution of the box2d body + + @param fDensity + Density in kg/m^2 + + @param fRestitution + Restitution (elasticity) coefficient, usually in the range [0,1] + */ void setDensityAndRestitution(const double fDensity, const double fRestitution); + /** Set restitution of the box2d body + + @param fRestitution + Restitution (elasticity) coefficient, usually in the range [0,1] + */ void setRestitution(const double fRestitution); /// Set type of the body |