[Libreoffice-commits] core.git: slideshow/Library_slideshow.mk slideshow/source

Sarper Akdemir (via logerrit) logerrit at kemper.freedesktop.org
Wed Aug 5 21:31:30 UTC 2020


 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.sarperakdemir at gmail.com>
AuthorDate: Fri Jun 5 20:23:21 2020 +0300
Commit:     Thorsten Behrens <Thorsten.Behrens at 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.Behrens at 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: */


More information about the Libreoffice-commits mailing list