diff options
Diffstat (limited to '')
-rw-r--r-- | slideshow/source/inc/box2dtools.hxx | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/slideshow/source/inc/box2dtools.hxx b/slideshow/source/inc/box2dtools.hxx new file mode 100644 index 000000000..e876468df --- /dev/null +++ b/slideshow/source/inc/box2dtools.hxx @@ -0,0 +1,483 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "shape.hxx" +#include "shapeattributelayer.hxx" +#include "attributemap.hxx" +#include <unordered_map> +#include <queue> + +class b2Body; +class b2World; + +namespace slideshow::internal +{ +class ShapeManager; +typedef std::shared_ptr<ShapeManager> ShapeManagerSharedPtr; +} + +namespace box2d::utils +{ +class box2DBody; +class box2DWorld; +typedef std::shared_ptr<box2DWorld> Box2DWorldSharedPtr; +typedef std::shared_ptr<box2DBody> Box2DBodySharedPtr; + +enum box2DBodyType +{ + BOX2D_STATIC_BODY = 0, + BOX2D_KINEMATIC_BODY, + BOX2D_DYNAMIC_BODY +}; + +enum box2DNonsimulatedShapeUpdateType +{ + BOX2D_UPDATE_POSITION_CHANGE, + BOX2D_UPDATE_POSITION, + BOX2D_UPDATE_ANGLE, + BOX2D_UPDATE_SIZE, + BOX2D_UPDATE_VISIBILITY, + BOX2D_UPDATE_LINEAR_VELOCITY, + BOX2D_UPDATE_ANGULAR_VELOCITY +}; + +/// Holds required information to perform an update to box2d +/// 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; + ::basegfx::B2DVector maVelocity; + double mfAngle; + double mfAngularVelocity; + 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 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 + 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 PhysicsAnimation that + /// is stepping the Box2D World. Used to create a lock mechanism + bool mbHasWorldStepper; + /// Flag used to stop overstepping that occurs when a physics + /// animation effect transfers step-lock to another one. + bool mbAlreadyStepped; + /// Number of Physics Animations going on + int mnPhysicsAnimationCounter; + std::unordered_map<css::uno::Reference<css::drawing::XShape>, Box2DBodySharedPtr> + mpXShapeToBodyMap; + + /** 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); + + /** 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 + + @param rOutPos + Position in LO user space coordinates + + @param fPassedTime + Time frame which the Box2D body should move to the specified position. + */ + 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 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); + + /** 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 + Angle of rotation in degrees. + + @param fPassedTime + 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 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); + + /** 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. + + @param xShape + Shape reference + + @param bCanCollide + true if collisions should be enabled for the corresponding Box2D body of this shape + and false if it should be disabled. + */ + void setShapeCollision(const css::uno::Reference<com::sun::star::drawing::XShape> xShape, + const bool bCanCollide); + + /** Process the updates queued in the maShapeParallelUpdateQueue + + Called on each step of the box2DWorld. + + @param fPassedTime + Time frame to process the updates accordingly (needed for proper simulations) + */ + void processUpdateQueue(const double fPassedTime); + + /** 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 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 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 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); + + void queueShapePositionUpdate(const css::uno::Reference<css::drawing::XShape>& xShape, + const ::basegfx::B2DPoint& rOutPos); + +public: + box2DWorld(const ::basegfx::B2DVector& rSlideSize); + ~box2DWorld(); + + bool initiateWorld(const ::basegfx::B2DVector& rSlideSize); + + /** Simulate and step through a given amount of time in the Box2D World + + @param fPassedTime + Amount of time to step through + + @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 World is initialized or not + bool isInitialized() const; + + /** Make the shape's corresponding box2D body a dynamic one. + + A dynamic body will be affected by other bodies and the gravity. + + @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, + const double fDensity, const double fBounciness); + + /** 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. 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 that is represented 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); + + /// Initiate all the shapes in the current slide in the box2DWorld as static ones + void initiateAllShapesAsStaticBodies( + const slideshow::internal::ShapeManagerSharedPtr& pShapeManager); + + /// @return whether the box2DWorld has a stepper or not + bool hasWorldStepper() const; + + /// Set the flag for whether the box2DWorld has a stepper or not + void setHasWorldStepper(const bool bHasWorldStepper); + + /// 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 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 appropriate 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 appropriate 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 appropriate 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); +}; + +/// Class that manages a single box2D Body +class box2DBody +{ +private: + /// Pointer to the body that this class manages + std::shared_ptr<b2Body> mpBox2DBody; + /// Scale factor for conversions between LO user space coordinates to Box2D World coordinates + double mfScaleFactor; + +public: + box2DBody(std::shared_ptr<b2Body> pBox2DBody, double fScaleFactor); + + /// @return current position in LO user space coordinates + ::basegfx::B2DPoint getPosition() const; + + /** Set the position of box2d body + + @param rPos + Position in LO user space coordinates + */ + void setPosition(const ::basegfx::B2DPoint& rPos); + + /** Moves body to the specified position + + 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 + + @param fPassedTime + Amount of time for the movement to take place + */ + void setPositionByLinearVelocity(const ::basegfx::B2DPoint& rDesiredPos, + const double fPassedTime); + + /** Sets linear velocity of the body + + @param rVelocity + Velocity vector in LO user space coordinates + */ + void setLinearVelocity(const ::basegfx::B2DVector& rVelocity); + + /** Rotate body to specified angle of rotation + + Rotates body to specified rotation as if the body had + angular velocity to reach that state in given time frame + + @param fDesiredAngle + 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 + + @param fAngularVelocity + Angular velocity in degrees per second + */ + void setAngularVelocity(const double fAngularVelocity); + + /// Sets whether the body have collisions or not + void setCollision(const bool bCanCollide); + + /// @return current angle of rotation of the body + double getAngle() const; + + /** 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 + void setType(box2DBodyType eType); + + /// @return type of the body + box2DBodyType getType() const; +}; + +/** Make the Box2D body a dynamic one + + A dynamic body will be affected by other bodies and the gravity. + + @param pBox2DBody + Pointer to the Box2D body + */ +Box2DBodySharedPtr makeBodyDynamic(const Box2DBodySharedPtr& pBox2DBody); + +/** Make the Box2D body a static one + + A static body will not be affected by other bodies and the gravity. + + @param pBox2DBody + Pointer to the Box2D body + */ +Box2DBodySharedPtr makeBodyStatic(const Box2DBodySharedPtr& pBox2DBody); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ |