slideshow/Library_slideshow.mk | 2 slideshow/source/engine/box2dtools.cxx | 465 +++++++++++++++++++++++++++++++++ slideshow/source/inc/box2dtools.hxx | 317 ++++++++++++++++++++++ 3 files changed, 784 insertions(+)
New commits: commit 49cdda7c4fcf401f6f8435f7830786fcf3b2450e Author: Sarper Akdemir <q.sarperakde...@gmail.com> AuthorDate: Fri Jun 5 20:23:21 2020 +0300 Commit: Thorsten Behrens <thorsten.behr...@cib.de> CommitDate: Wed Aug 5 23:30:45 2020 +0200 box2d tools: initial work for physics based animation effects Two new classes for managing box2d bodies(b2Body) and box2d worlds(b2World) ::box2d::utils::Box2DBody : Manages box2d bodies (b2Body) ::box2d::utils::Box2DWorld : Manages box2d world (b2World) Change-Id: Id02fefe937347029daddde043da2b8e8dba3acaf Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95614 Tested-by: Jenkins Reviewed-by: Thorsten Behrens <thorsten.behr...@cib.de> diff --git a/slideshow/Library_slideshow.mk b/slideshow/Library_slideshow.mk index 55c531a86f43..53324ea25dcc 100644 --- a/slideshow/Library_slideshow.mk +++ b/slideshow/Library_slideshow.mk @@ -24,6 +24,7 @@ $(eval $(call gb_Library_set_precompiled_header,slideshow,slideshow/inc/pch/prec $(eval $(call gb_Library_use_externals,slideshow,\ boost_headers \ + box2d \ )) ifeq ($(DISABLE_GUI),) $(eval $(call gb_Library_use_externals,slideshow,\ @@ -84,6 +85,7 @@ $(eval $(call gb_Library_add_exception_objects,slideshow,\ slideshow/source/engine/animationnodes/propertyanimationnode \ slideshow/source/engine/animationnodes/sequentialtimecontainer \ slideshow/source/engine/attributemap \ + slideshow/source/engine/box2dtools \ slideshow/source/engine/color \ slideshow/source/engine/delayevent \ slideshow/source/engine/effectrewinder \ diff --git a/slideshow/source/engine/box2dtools.cxx b/slideshow/source/engine/box2dtools.cxx new file mode 100644 index 000000000000..8729300184f6 --- /dev/null +++ b/slideshow/source/engine/box2dtools.cxx @@ -0,0 +1,465 @@ +/* -*- 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/. + */ + +#include <box2dtools.hxx> +#include <Box2D/Box2D.h> + +#include <shapemanager.hxx> + +#define BOX2D_SLIDE_SIZE_IN_METERS 100.00f + +namespace box2d::utils +{ +namespace +{ +double calculateScaleFactor(const ::basegfx::B2DVector& rSlideSize) +{ + double fWidth = rSlideSize.getX(); + double fHeight = rSlideSize.getY(); + + if (fWidth > fHeight) + return BOX2D_SLIDE_SIZE_IN_METERS / fWidth; + else + return BOX2D_SLIDE_SIZE_IN_METERS / fHeight; +} + +b2BodyType getBox2DInternalBodyType(const box2DBodyType eType) +{ + switch (eType) + { + default: + case BOX2D_STATIC_BODY: + return b2_staticBody; + case BOX2D_KINEMATIC_BODY: + return b2_kinematicBody; + case BOX2D_DYNAMIC_BODY: + return b2_dynamicBody; + } +} + +box2DBodyType getBox2DLOBodyType(const b2BodyType eType) +{ + switch (eType) + { + default: + case b2_staticBody: + return BOX2D_STATIC_BODY; + case b2_kinematicBody: + return BOX2D_KINEMATIC_BODY; + case b2_dynamicBody: + return BOX2D_DYNAMIC_BODY; + } +} + +b2Vec2 convertB2DPointToBox2DVec2(const basegfx::B2DPoint& aPoint, const double fScaleFactor) +{ + return { static_cast<float>(aPoint.getX() * fScaleFactor), + static_cast<float>(aPoint.getY() * -fScaleFactor) }; +} +} + +box2DWorld::box2DWorld(const ::basegfx::B2DVector& rSlideSize) + : mpBox2DWorld() + , mfScaleFactor(calculateScaleFactor(rSlideSize)) + , mbShapesInitialized(false) + , mbHasWorldStepper(false) + , mpXShapeToBodyMap() + , maShapeUpdateQueue() +{ +} + +box2DWorld::~box2DWorld() = default; + +bool box2DWorld::initiateWorld(const ::basegfx::B2DVector& rSlideSize) +{ + if (!mpBox2DWorld) + { + mpBox2DWorld = std::make_unique<b2World>(b2Vec2(0.0f, -30.0f)); + createStaticFrameAroundSlide(rSlideSize); + return false; + } + else + { + return true; + } +} + +void box2DWorld::createStaticFrameAroundSlide(const ::basegfx::B2DVector& rSlideSize) +{ + assert(mpBox2DWorld); + + float fWidth = static_cast<float>(rSlideSize.getX() * mfScaleFactor); + float fHeight = static_cast<float>(rSlideSize.getY() * mfScaleFactor); + + // static body for creating the frame around the slide + b2BodyDef aBodyDef; + aBodyDef.type = b2_staticBody; + aBodyDef.position.Set(0, 0); + + // not going to be stored anywhere, Box2DWorld will handle this body + b2Body* pStaticBody = mpBox2DWorld->CreateBody(&aBodyDef); + + // create an edge loop that represents slide frame + b2Vec2 aEdgePoints[4]; + aEdgePoints[0].Set(0, 0); + aEdgePoints[1].Set(0, -fHeight); + aEdgePoints[2].Set(fWidth, -fHeight); + aEdgePoints[3].Set(fWidth, 0); + + b2ChainShape aEdgesChainShape; + aEdgesChainShape.CreateLoop(aEdgePoints, 4); + + b2FixtureDef aFixtureDef; + aFixtureDef.shape = &aEdgesChainShape; + pStaticBody->CreateFixture(&aFixtureDef); +} + +void box2DWorld::setShapePositionByLinearVelocity( + const css::uno::Reference<com::sun::star::drawing::XShape> xShape, + const basegfx::B2DPoint& rOutPos, const double fPassedTime) +{ + assert(mpBox2DWorld); + if (fPassedTime > 0) // this only makes sense if there was an advance in time + { + Box2DBodySharedPtr pBox2DBody = mpXShapeToBodyMap.find(xShape)->second; + pBox2DBody->setPositionByLinearVelocity(rOutPos, fPassedTime); + } +} + +void box2DWorld::setShapeLinearVelocity( + const css::uno::Reference<com::sun::star::drawing::XShape> xShape, + const basegfx::B2DVector& rVelocity) +{ + assert(mpBox2DWorld); + Box2DBodySharedPtr pBox2DBody = mpXShapeToBodyMap.find(xShape)->second; + pBox2DBody->setLinearVelocity(rVelocity); +} + +void box2DWorld::setShapeAngleByAngularVelocity( + const css::uno::Reference<com::sun::star::drawing::XShape> xShape, const double fAngle, + const double fPassedTime) +{ + assert(mpBox2DWorld); + if (fPassedTime > 0) // this only makes sense if there was an advance in time + { + Box2DBodySharedPtr pBox2DBody = mpXShapeToBodyMap.find(xShape)->second; + pBox2DBody->setAngleByAngularVelocity(fAngle, fPassedTime); + } +} + +void box2DWorld::setShapeAngularVelocity( + const css::uno::Reference<com::sun::star::drawing::XShape> xShape, + const double fAngularVelocity) +{ + assert(mpBox2DWorld); + Box2DBodySharedPtr pBox2DBody = mpXShapeToBodyMap.find(xShape)->second; + pBox2DBody->setAngularVelocity(fAngularVelocity); +} + +void box2DWorld::setShapeCollision( + const css::uno::Reference<com::sun::star::drawing::XShape> xShape, bool bCanCollide) +{ + assert(mpBox2DWorld); + Box2DBodySharedPtr pBox2DBody = mpXShapeToBodyMap.find(xShape)->second; + pBox2DBody->setCollision(bCanCollide); +} + +void box2DWorld::processUpdateQueue(const double fPassedTime) +{ + while (!maShapeUpdateQueue.empty()) + { + Box2DShapeUpdateInformation& aQueueElement = maShapeUpdateQueue.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); + } + maShapeUpdateQueue.pop(); + } + } +} + +void box2DWorld::initateAllShapesAsStaticBodies( + const slideshow::internal::ShapeManagerSharedPtr pShapeManager) +{ + assert(mpBox2DWorld); + + mbShapesInitialized = true; + auto aXShapeToShapeMap = pShapeManager->getXShapeToShapeMap(); + + // iterate over shapes in the current slide + for (auto aIt = aXShapeToShapeMap.begin(); aIt != aXShapeToShapeMap.end(); aIt++) + { + slideshow::internal::ShapeSharedPtr pShape = aIt->second; + if (pShape->isForeground()) + { + Box2DBodySharedPtr pBox2DBody = createStaticBodyFromBoundingBox(pShape); + mpXShapeToBodyMap.insert(std::make_pair(pShape->getXShape(), pBox2DBody)); + if (!pShape->isVisible()) + { + // if the shape isn't visible, mark it + queueShapeVisibilityUpdate(pShape->getXShape(), false); + } + } + } +} + +bool box2DWorld::hasWorldStepper() { return mbHasWorldStepper; } + +void box2DWorld::setHasWorldStepper(const bool bHasWorldStepper) +{ + mbHasWorldStepper = bHasWorldStepper; +} + +void box2DWorld::queuePositionUpdate(css::uno::Reference<com::sun::star::drawing::XShape> xShape, + const basegfx::B2DPoint& rOutPos) +{ + Box2DShapeUpdateInformation aQueueElement = { xShape, {}, BOX2D_UPDATE_POSITION }; + aQueueElement.maPosition = rOutPos; + maShapeUpdateQueue.push(aQueueElement); +} + +void box2DWorld::queueLinearVelocityUpdate( + css::uno::Reference<com::sun::star::drawing::XShape> xShape, + const basegfx::B2DVector& rVelocity) +{ + Box2DShapeUpdateInformation aQueueElement = { xShape, {}, BOX2D_UPDATE_LINEAR_VELOCITY, 1 }; + aQueueElement.maVelocity = rVelocity; + maShapeUpdateQueue.push(aQueueElement); +} + +void box2DWorld::queueRotationUpdate(css::uno::Reference<com::sun::star::drawing::XShape> xShape, + const double fAngle) +{ + Box2DShapeUpdateInformation aQueueElement = { xShape, {}, BOX2D_UPDATE_ANGLE }; + aQueueElement.mfAngle = fAngle; + maShapeUpdateQueue.push(aQueueElement); +} + +void box2DWorld::queueAngularVelocityUpdate( + css::uno::Reference<com::sun::star::drawing::XShape> xShape, const double fAngularVelocity) +{ + Box2DShapeUpdateInformation aQueueElement = { xShape, {}, BOX2D_UPDATE_ANGULAR_VELOCITY, 1 }; + aQueueElement.mfAngularVelocity = fAngularVelocity; + maShapeUpdateQueue.push(aQueueElement); +} + +void box2DWorld::queueShapeVisibilityUpdate( + css::uno::Reference<com::sun::star::drawing::XShape> xShape, const bool bVisibility) +{ + Box2DShapeUpdateInformation aQueueElement = { xShape, {}, BOX2D_UPDATE_VISIBILITY }; + aQueueElement.mbVisibility = bVisibility; + maShapeUpdateQueue.push(aQueueElement); +} + +void box2DWorld::step(const float fTimeStep, const int nVelocityIterations, + const int nPositionIterations) +{ + assert(mpBox2DWorld); + mpBox2DWorld->Step(fTimeStep, nVelocityIterations, nPositionIterations); +} + +double box2DWorld::stepAmount(const double fPassedTime, const float fTimeStep, + const int nVelocityIterations, const int nPositionIterations) +{ + assert(mpBox2DWorld); + + unsigned int nStepAmount = static_cast<unsigned int>(std::round(fPassedTime / fTimeStep)); + double fTimeSteppedThrough = fTimeStep * nStepAmount; + + processUpdateQueue(fTimeSteppedThrough); + + for (unsigned int nStepCounter = 0; nStepCounter < nStepAmount; nStepCounter++) + { + step(fTimeStep, nVelocityIterations, nPositionIterations); + } + + return fTimeSteppedThrough; +} + +bool box2DWorld::shapesInitialized() { return mbShapesInitialized; } + +bool box2DWorld::isInitialized() +{ + if (mpBox2DWorld) + return true; + else + return false; +} + +Box2DBodySharedPtr box2DWorld::makeShapeDynamic(const slideshow::internal::ShapeSharedPtr pShape) +{ + assert(mpBox2DWorld); + Box2DBodySharedPtr pBox2DBody = mpXShapeToBodyMap.find(pShape->getXShape())->second; + return makeBodyDynamic(pBox2DBody); +} + +Box2DBodySharedPtr box2DWorld::makeBodyDynamic(const Box2DBodySharedPtr pBox2DBody) +{ + assert(mpBox2DWorld); + if (pBox2DBody->getType() != BOX2D_DYNAMIC_BODY) + { + pBox2DBody->setType(BOX2D_DYNAMIC_BODY); + } + return pBox2DBody; +} + +Box2DBodySharedPtr box2DWorld::makeShapeStatic(const slideshow::internal::ShapeSharedPtr pShape) +{ + assert(mpBox2DWorld); + Box2DBodySharedPtr pBox2DBody = mpXShapeToBodyMap.find(pShape->getXShape())->second; + return makeBodyStatic(pBox2DBody); +} + +Box2DBodySharedPtr box2DWorld::makeBodyStatic(const Box2DBodySharedPtr pBox2DBody) +{ + assert(mpBox2DWorld); + if (pBox2DBody->getType() != BOX2D_STATIC_BODY) + { + pBox2DBody->setType(BOX2D_STATIC_BODY); + } + return pBox2DBody; +} + +Box2DBodySharedPtr +box2DWorld::createStaticBodyFromBoundingBox(const slideshow::internal::ShapeSharedPtr& rShape, + const float fDensity, const float fFriction) +{ + assert(mpBox2DWorld); + ::basegfx::B2DRectangle aShapeBounds = rShape->getBounds(); + double fShapeWidth = aShapeBounds.getWidth() * mfScaleFactor; + double fShapeHeight = aShapeBounds.getHeight() * mfScaleFactor; + + b2BodyDef aBodyDef; + aBodyDef.type = b2_staticBody; + aBodyDef.position = convertB2DPointToBox2DVec2(aShapeBounds.getCenter(), mfScaleFactor); + + std::shared_ptr<b2Body> pBody(mpBox2DWorld->CreateBody(&aBodyDef), [](b2Body* pB2Body) { + pB2Body->GetWorld()->DestroyBody(pB2Body); + }); + + b2PolygonShape aDynamicBox; + aDynamicBox.SetAsBox(static_cast<float>(fShapeWidth / 2), static_cast<float>(fShapeHeight / 2)); + + b2FixtureDef aFixtureDef; + aFixtureDef.shape = &aDynamicBox; + aFixtureDef.density = fDensity; + aFixtureDef.friction = fFriction; + aFixtureDef.restitution = 0.1f; + + pBody->CreateFixture(&aFixtureDef); + return std::make_shared<box2DBody>(pBody, mfScaleFactor); +} + +box2DBody::box2DBody(std::shared_ptr<b2Body> pBox2DBody, double fScaleFactor) + : mpBox2DBody(pBox2DBody) + , mfScaleFactor(fScaleFactor) +{ +} + +::basegfx::B2DPoint box2DBody::getPosition() +{ + b2Vec2 aPosition = mpBox2DBody->GetPosition(); + double fX = static_cast<double>(aPosition.x) / mfScaleFactor; + double fY = static_cast<double>(aPosition.y) / -mfScaleFactor; + return ::basegfx::B2DPoint(fX, fY); +} + +void box2DBody::setPositionByLinearVelocity(const basegfx::B2DPoint& rDesiredPos, + const double fPassedTime) +{ + if (mpBox2DBody->GetType() != b2_kinematicBody) + mpBox2DBody->SetType(b2_kinematicBody); + + ::basegfx::B2DPoint aCurrentPos = getPosition(); + ::basegfx::B2DVector aVelocity = (rDesiredPos - aCurrentPos) / fPassedTime; + + setLinearVelocity(aVelocity); +} + +void box2DBody::setAngleByAngularVelocity(const double fDesiredAngle, const double fPassedTime) +{ + if (mpBox2DBody->GetType() != b2_kinematicBody) + mpBox2DBody->SetType(b2_kinematicBody); + + double fDeltaAngle = fDesiredAngle - getAngle(); + + // temporary hack for repeating animation effects + while (fDeltaAngle > 180 + || fDeltaAngle < -180) // if it is bigger than 180 opposite rotation is actually closer + fDeltaAngle += fDeltaAngle > 0 ? -360 : +360; + + double fAngularVelocity = fDeltaAngle / fPassedTime; + setAngularVelocity(fAngularVelocity); +} + +void box2DBody::setLinearVelocity(const ::basegfx::B2DVector& rVelocity) +{ + b2Vec2 aVelocity = { static_cast<float>(rVelocity.getX() * mfScaleFactor), + static_cast<float>(rVelocity.getY() * -mfScaleFactor) }; + mpBox2DBody->SetLinearVelocity(aVelocity); +} + +void box2DBody::setAngularVelocity(const double fAngularVelocity) +{ + float fBox2DAngularVelocity = static_cast<float>(basegfx::deg2rad(-fAngularVelocity)); + mpBox2DBody->SetAngularVelocity(fBox2DAngularVelocity); +} + +void box2DBody::setCollision(const bool bCanCollide) +{ + for (b2Fixture* pFixture = mpBox2DBody->GetFixtureList(); pFixture; + pFixture = pFixture->GetNext()) + { + b2Filter aFilter = pFixture->GetFilterData(); + aFilter.maskBits = bCanCollide ? 0xFFFF : 0x0000; + pFixture->SetFilterData(aFilter); + } +} + +double box2DBody::getAngle() +{ + double fAngle = static_cast<double>(mpBox2DBody->GetAngle()); + return ::basegfx::rad2deg(-fAngle); +} + +void box2DBody::setType(box2DBodyType eType) +{ + mpBox2DBody->SetType(getBox2DInternalBodyType(eType)); +} + +box2DBodyType box2DBody::getType() { return getBox2DLOBodyType(mpBox2DBody->GetType()); } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/slideshow/source/inc/box2dtools.hxx b/slideshow/source/inc/box2dtools.hxx new file mode 100644 index 000000000000..0824a3c260c5 --- /dev/null +++ b/slideshow/source/inc/box2dtools.hxx @@ -0,0 +1,317 @@ +/* -*- 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 <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, + 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 Box2DShapeUpdateInformation +{ + css::uno::Reference<css::drawing::XShape> mxShape; + union { + ::basegfx::B2DPoint maPosition; + ::basegfx::B2DVector maVelocity; + double mfAngle; + double mfAngularVelocity; + bool mbVisibility; + }; + box2DNonsimulatedShapeUpdateType meUpdateType; + 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. + */ +class box2DWorld +{ +private: + /// Pointer to the real Box2D World that this class manages for simulations + std::unique_ptr<b2World> mpBox2DWorld; + /// Scale factor for conversions between LO user space coordinates to Box2D World coordinates + double mfScaleFactor; + bool mbShapesInitialized; + 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 + std::queue<Box2DShapeUpdateInformation> maShapeUpdateQueue; + + /// 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 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 + + @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 body in the Box2D world + void setShapeLinearVelocity(const css::uno::Reference<com::sun::star::drawing::XShape> xShape, + const basegfx::B2DVector& rVelocity); + + /** 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 + + @param xShape + Shape reference + + @param fAngle + Position in LO user space coordinates + + @param fPassedTime + Time frame which the Box2D body should move to the specified position. + */ + 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 + 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 + + 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 maShapeUpdateQueue + + 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 + void step(const float fTimeStep = 1.0f / 100.0f, const int nVelocityIterations = 6, + const int nPositionIterations = 2); + +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 + */ + 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 + bool isInitialized(); + + /** Make the Box2D body corresponding to the given shape 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 + */ + Box2DBodySharedPtr makeShapeDynamic(const slideshow::internal::ShapeSharedPtr pShape); + + /** 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 corresponding to the given shape a static one + + A static body will not be affected by other bodies and the gravity. + + @param pShape + Pointer to the shape to alter the corresponding Box2D body of + */ + Box2DBodySharedPtr makeShapeStatic(const slideshow::internal::ShapeSharedPtr pShape); + + /** Make the Box2D body a dynamic 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); + + /// Create a static body from the given shape's bounding box + Box2DBodySharedPtr + createStaticBodyFromBoundingBox(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 + initateAllShapesAsStaticBodies(const slideshow::internal::ShapeManagerSharedPtr pShapeManager); + + /// @return whether the box2DWorld has a stepper or not + bool hasWorldStepper(); + + /// 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 + void queuePositionUpdate(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 + void queueLinearVelocityUpdate(css::uno::Reference<css::drawing::XShape> xShape, + const ::basegfx::B2DVector& rVelocity); + + /// Queue a rotation update on the next step of the box2DWorld for the corresponding body + void queueRotationUpdate(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 + void queueAngularVelocityUpdate(css::uno::Reference<com::sun::star::drawing::XShape> xShape, + const double fAngularVelocity); + + /// Queue an update that changes collision of the corresponding body + /// on the next step of the box2DWorld, used for animations that change visibility + void queueShapeVisibilityUpdate(css::uno::Reference<css::drawing::XShape> xShape, + const bool bVisibility); +}; + +/// 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(); + + /** Sets body to specified position + + Sets body to specified position as if the body had + velocity to reach that point in given time frame + + @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 + void setLinearVelocity(const ::basegfx::B2DVector& rVelocity); + + /** Sets body to specified angle of rotation + + Sets 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 + + @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 + 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(); + + /// Set type of the body + void setType(box2DBodyType eType); + + /// @return type of the body + box2DBodyType getType(); +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits