summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSarper Akdemir <q.sarperakdemir@gmail.com>2020-06-05 20:23:21 +0300
committerThorsten Behrens <Thorsten.Behrens@CIB.de>2020-08-05 23:30:45 +0200
commit49cdda7c4fcf401f6f8435f7830786fcf3b2450e (patch)
treeefdf386e113ce8d9f517a372ee1018991eb117f9
parentc6cec621aa19ff09b82044f91228c4a08df6bbb7 (diff)
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.Behrens@CIB.de>
-rw-r--r--slideshow/Library_slideshow.mk2
-rw-r--r--slideshow/source/engine/box2dtools.cxx465
-rw-r--r--slideshow/source/inc/box2dtools.hxx317
3 files changed, 784 insertions, 0 deletions
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: */