[Libreoffice-commits] core.git: Branch 'private/quwex/gsoc-box2d-experimental' - 3 commits - offapi/com slideshow/source

Sarper Akdemir (via logerrit) logerrit at kemper.freedesktop.org
Mon Aug 10 22:39:45 UTC 2020


Rebased ref, commits from common ancestor:
commit 786cfdcf2ac6ce0e3d8a0cc48b5110b685db2aae
Author:     Sarper Akdemir <q.sarperakdemir at gmail.com>
AuthorDate: Mon Aug 10 15:30:26 2020 +0300
Commit:     Sarper Akdemir <q.sarperakdemir at gmail.com>
CommitDate: Tue Aug 11 01:38:59 2020 +0300

    add version tag to ANIMATEPHYSICS
    
    Change-Id: Ia6db8ca10a0311ae8492cdc5ab518efaba611cb2

diff --git a/offapi/com/sun/star/animations/AnimationNodeType.idl b/offapi/com/sun/star/animations/AnimationNodeType.idl
index d0cd6e268fd6..7c6abb105947 100644
--- a/offapi/com/sun/star/animations/AnimationNodeType.idl
+++ b/offapi/com/sun/star/animations/AnimationNodeType.idl
@@ -68,7 +68,10 @@ constants AnimationNodeType
     /** Defines a command effect. */
     const short COMMAND = 11;
 
-    /** Defines a physics animation */
+    /** Defines a physics animation
+
+        @since LibreOffice 7.1
+    */
     const short ANIMATEPHYSICS = 12;
 
 };
commit a5392206b824075e930880b5b379813982abb72f
Author:     Sarper Akdemir <q.sarperakdemir at gmail.com>
AuthorDate: Thu Aug 6 10:32:55 2020 +0300
Commit:     Sarper Akdemir <q.sarperakdemir at gmail.com>
CommitDate: Tue Aug 11 01:38:59 2020 +0300

    make physics based animation effects always processed last
    
    Change-Id: I92d436aced6ef3ee2c8b0bf0167c1f7e642ba3b5

diff --git a/slideshow/source/engine/activitiesqueue.cxx b/slideshow/source/engine/activitiesqueue.cxx
index ba982385356e..38e79d1e5677 100644
--- a/slideshow/source/engine/activitiesqueue.cxx
+++ b/slideshow/source/engine/activitiesqueue.cxx
@@ -50,6 +50,8 @@ namespace slideshow::internal
             {
                 for( const auto& pActivity : maCurrentActivitiesWaiting )
                     pActivity->dispose();
+                for( const auto& pActivity : maCurrentActivitiesToBeProcessedLast )
+                    pActivity->dispose();
                 for( const auto& pActivity : maCurrentActivitiesReinsert )
                     pActivity->dispose();
             }
@@ -59,7 +61,7 @@ namespace slideshow::internal
             }
         }
 
-        bool ActivitiesQueue::addActivity( const ActivitySharedPtr& pActivity )
+        bool ActivitiesQueue::addActivity( const ActivitySharedPtr& pActivity, const bool bProcessLast )
         {
             OSL_ENSURE( pActivity, "ActivitiesQueue::addActivity: activity ptr NULL" );
 
@@ -67,7 +69,17 @@ namespace slideshow::internal
                 return false;
 
             // add entry to waiting list
-            maCurrentActivitiesWaiting.push_back( pActivity );
+            if( !bProcessLast )
+            {
+                maCurrentActivitiesWaiting.push_back( pActivity );
+            }
+            else
+            {
+                // Activities that should be processed last is kept in a different
+                // ActivityQueue, and later added to the end of the maCurrentActivitiesWaiting
+                // at the start of ActivitiesQueue::process()
+                maCurrentActivitiesToBeProcessedLast.push_back( pActivity );
+            }
 
             return true;
         }
@@ -76,6 +88,12 @@ namespace slideshow::internal
         {
             SAL_INFO("slideshow.verbose", "ActivitiesQueue: outer loop heartbeat" );
 
+            // If there are activities to be processed last add them to the end of the ActivitiesQueue
+            maCurrentActivitiesWaiting.insert( maCurrentActivitiesWaiting.end(),
+                                               maCurrentActivitiesToBeProcessedLast.begin(),
+                                               maCurrentActivitiesToBeProcessedLast.end() );
+            maCurrentActivitiesToBeProcessedLast.clear();
+
             // accumulate time lag for all activities, and lag time
             // base if necessary:
             double fLag = 0.0;
diff --git a/slideshow/source/engine/animationnodes/animationbasenode.cxx b/slideshow/source/engine/animationnodes/animationbasenode.cxx
index 4dcb640795aa..7999b5a7654a 100644
--- a/slideshow/source/engine/animationnodes/animationbasenode.cxx
+++ b/slideshow/source/engine/animationnodes/animationbasenode.cxx
@@ -23,6 +23,7 @@
 #include <com/sun/star/animations/Timing.hpp>
 #include <com/sun/star/animations/AnimationAdditiveMode.hpp>
 #include <com/sun/star/presentation/ShapeAnimationSubType.hpp>
+#include <com/sun/star/animations/AnimationNodeType.hpp>
 
 #include "nodetools.hxx"
 #include <doctreenode.hxx>
@@ -294,7 +295,10 @@ void AnimationBaseNode::activate_st()
         mpActivity->setTargets( getShape(), maAttributeLayerHolder.get() );
 
         // add to activities queue
-        getContext().mrActivitiesQueue.addActivity( mpActivity );
+        if( mxAnimateNode->getType() == css::animations::AnimationNodeType::ANIMATEPHYSICS )
+            getContext().mrActivitiesQueue.addActivity(mpActivity, true);
+        else
+            getContext().mrActivitiesQueue.addActivity( mpActivity );
     }
     else {
         // Actually, DO generate the event for empty activity,
diff --git a/slideshow/source/inc/activitiesqueue.hxx b/slideshow/source/inc/activitiesqueue.hxx
index b4f88b1b39d1..76dc981f8f65 100644
--- a/slideshow/source/inc/activitiesqueue.hxx
+++ b/slideshow/source/inc/activitiesqueue.hxx
@@ -57,7 +57,7 @@ namespace slideshow
 
             /** Add the given activity to the queue.
              */
-            bool addActivity( const ActivitySharedPtr& pActivity );
+            bool addActivity( const ActivitySharedPtr& pActivity, const bool bProcessLast = false );
 
             /** Process the activities queue.
 
@@ -96,6 +96,11 @@ namespace slideshow
                                                                  // await processing for this
                                                                  // round
 
+            ActivityQueue           maCurrentActivitiesToBeProcessedLast; // activities that will be
+                                                                          // added to the end of
+                                                                          // maCurrentActivitiesWaiting at
+                                                                          // the start of process()
+
             ActivityQueue           maCurrentActivitiesReinsert;    // currently running
                                                                       // activities, that are
                                                                       // already processed for
commit 8452d5ddf05b3b648a61a4cae9e1df2855ee670c
Author:     Sarper Akdemir <q.sarperakdemir at gmail.com>
AuthorDate: Mon Jul 27 23:02:48 2020 +0300
Commit:     Sarper Akdemir <q.sarperakdemir at gmail.com>
CommitDate: Tue Aug 11 01:38:59 2020 +0300

    add support for complex shapes in physics animation effects
    
    Makes it possible to represent complex shapes of LibreOffice
    in physics animation effects by closely approximating their geometry.
    
    Therefore enables more convincing animation effects.
    
    Change-Id: I807bbde92c143b8c96792b3d8bf9603a31216486

diff --git a/slideshow/source/engine/box2dtools.cxx b/slideshow/source/engine/box2dtools.cxx
index c188234105d7..d36648b60f85 100644
--- a/slideshow/source/engine/box2dtools.cxx
+++ b/slideshow/source/engine/box2dtools.cxx
@@ -11,6 +11,13 @@
 #include <Box2D/Box2D.h>
 
 #include <shapemanager.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygontriangulator.hxx>
+#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
+
+#include <svx/svdobj.hxx>
+#include <svx/svdoashp.hxx>
 
 #define BOX2D_SLIDE_SIZE_IN_METERS 100.00f
 
@@ -62,6 +69,132 @@ b2Vec2 convertB2DPointToBox2DVec2(const basegfx::B2DPoint& aPoint, const double
     return { static_cast<float>(aPoint.getX() * fScaleFactor),
              static_cast<float>(aPoint.getY() * -fScaleFactor) };
 }
+
+// expects rTriangleVector to have coordinates relative to the shape's bounding box center
+void addTriangleVectorToBody(const basegfx::triangulator::B2DTriangleVector& rTriangleVector,
+                             b2Body* aBody, const float fDensity, const float fFriction,
+                             const float fRestitution, const double fScaleFactor)
+{
+    for (const basegfx::triangulator::B2DTriangle& aTriangle : rTriangleVector)
+    {
+        b2FixtureDef aFixture;
+        b2PolygonShape aPolygonShape;
+        b2Vec2 aTriangleVertices[3]
+            = { convertB2DPointToBox2DVec2(aTriangle.getA(), fScaleFactor),
+                convertB2DPointToBox2DVec2(aTriangle.getB(), fScaleFactor),
+                convertB2DPointToBox2DVec2(aTriangle.getC(), fScaleFactor) };
+
+        bool bValidPointDistance = true;
+        for (int nPointIndexA = 0; nPointIndexA < 3; nPointIndexA++)
+        {
+            for (int nPointIndexB = 0; nPointIndexB < 3; nPointIndexB++)
+            {
+                if (nPointIndexA == nPointIndexB)
+                    continue;
+
+                // check whether the triangle would be a degenerately small one
+                if (b2DistanceSquared(aTriangleVertices[nPointIndexA],
+                                      aTriangleVertices[nPointIndexB])
+                    < 0.003f)
+                {
+                    bValidPointDistance = false;
+                }
+            }
+        }
+        if (bValidPointDistance)
+        {
+            aPolygonShape.Set(aTriangleVertices, 3);
+            aFixture.shape = &aPolygonShape;
+            aFixture.density = fDensity;
+            aFixture.friction = fFriction;
+            aFixture.restitution = fRestitution;
+            aBody->CreateFixture(&aFixture);
+        }
+    }
+}
+
+// expects rPolygon to have coordinates relative to it's center
+void addEdgeShapeToBody(const basegfx::B2DPolygon& rPolygon, b2Body* aBody, const float fDensity,
+                        const float fFriction, const float fRestitution, const double fScaleFactor)
+{
+    basegfx::B2DPolygon aPolygon = basegfx::utils::removeNeutralPoints(rPolygon);
+
+    // value that somewhat defines half width of the quadrilateral
+    // that will be representing edge segment in the box2d world
+    const float fHalfWidth = 0.1;
+    bool bHasPreviousQuadrilateralEdge = false;
+    b2Vec2 aQuadrilateralVertices[4];
+
+    for (sal_uInt32 nIndex = 0; nIndex < aPolygon.count(); nIndex++)
+    {
+        b2FixtureDef aFixture;
+        b2PolygonShape aPolygonShape;
+
+        basegfx::B2DPoint aPointA;
+        basegfx::B2DPoint aPointB;
+        if (nIndex != 0)
+        {
+            aPointA = aPolygon.getB2DPoint(nIndex - 1);
+            aPointB = aPolygon.getB2DPoint(nIndex);
+        }
+        else if (/* nIndex == 0 && */ aPolygon.isClosed())
+        {
+            // start by connecting the last point to the first one
+            aPointA = aPolygon.getB2DPoint(aPolygon.count() - 1);
+            aPointB = aPolygon.getB2DPoint(nIndex);
+        }
+        else // the polygon isn't closed, won't connect last and first points
+        {
+            continue;
+        }
+
+        b2Vec2 aEdgeUnitVec(convertB2DPointToBox2DVec2(aPointB, fScaleFactor)
+                            - convertB2DPointToBox2DVec2(aPointA, fScaleFactor));
+        aEdgeUnitVec.Normalize();
+
+        b2Vec2 aEdgeNormal(-aEdgeUnitVec.y, aEdgeUnitVec.x);
+
+        if (!bHasPreviousQuadrilateralEdge)
+        {
+            aQuadrilateralVertices[0]
+                = convertB2DPointToBox2DVec2(aPointA, fScaleFactor) + fHalfWidth * aEdgeNormal;
+            aQuadrilateralVertices[1]
+                = convertB2DPointToBox2DVec2(aPointA, fScaleFactor) + -fHalfWidth * aEdgeNormal;
+            bHasPreviousQuadrilateralEdge = true;
+        }
+        aQuadrilateralVertices[2]
+            = convertB2DPointToBox2DVec2(aPointB, fScaleFactor) + fHalfWidth * aEdgeNormal;
+        aQuadrilateralVertices[3]
+            = convertB2DPointToBox2DVec2(aPointB, fScaleFactor) + -fHalfWidth * aEdgeNormal;
+
+        bool bValidPointDistance
+            = b2DistanceSquared(aQuadrilateralVertices[0], aQuadrilateralVertices[2]) > 0.003f;
+
+        if (bValidPointDistance)
+        {
+            aPolygonShape.Set(aQuadrilateralVertices, 4);
+            aFixture.shape = &aPolygonShape;
+            aFixture.density = fDensity;
+            aFixture.friction = fFriction;
+            aFixture.restitution = fRestitution;
+            aBody->CreateFixture(&aFixture);
+
+            // prepare the quadrilateral edge for next connection
+            aQuadrilateralVertices[0] = aQuadrilateralVertices[2];
+            aQuadrilateralVertices[1] = aQuadrilateralVertices[3];
+        }
+    }
+}
+
+void addEdgeShapeToBody(const basegfx::B2DPolyPolygon& rPolyPolygon, b2Body* aBody,
+                        const float fDensity, const float fFriction, const float fRestitution,
+                        const double fScaleFactor)
+{
+    for (const basegfx::B2DPolygon& rPolygon : rPolyPolygon)
+    {
+        addEdgeShapeToBody(rPolygon, aBody, fDensity, fFriction, fRestitution, fScaleFactor);
+    }
+}
 }
 
 box2DWorld::box2DWorld(const ::basegfx::B2DVector& rSlideSize)
@@ -224,7 +357,8 @@ void box2DWorld::initateAllShapesAsStaticBodies(
         slideshow::internal::ShapeSharedPtr pShape = aIt->second;
         if (pShape->isForeground())
         {
-            Box2DBodySharedPtr pBox2DBody = createStaticBodyFromBoundingBox(pShape);
+            Box2DBodySharedPtr pBox2DBody = createStaticBody(pShape);
+
             mpXShapeToBodyMap.insert(std::make_pair(pShape->getXShape(), pBox2DBody));
             if (!pShape->isVisible())
             {
@@ -394,14 +528,12 @@ Box2DBodySharedPtr box2DWorld::makeBodyStatic(const Box2DBodySharedPtr& pBox2DBo
     return pBox2DBody;
 }
 
-Box2DBodySharedPtr
-box2DWorld::createStaticBodyFromBoundingBox(const slideshow::internal::ShapeSharedPtr& rShape,
-                                            const float fDensity, const float fFriction)
+Box2DBodySharedPtr box2DWorld::createStaticBody(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;
@@ -411,16 +543,63 @@ box2DWorld::createStaticBodyFromBoundingBox(const slideshow::internal::ShapeShar
         pB2Body->GetWorld()->DestroyBody(pB2Body);
     });
 
-    b2PolygonShape aDynamicBox;
-    aDynamicBox.SetAsBox(static_cast<float>(fShapeWidth / 2), static_cast<float>(fShapeHeight / 2));
+    SdrObject* pSdrObject = SdrObject::getSdrObjectFromXShape(rShape->getXShape());
 
-    b2FixtureDef aFixtureDef;
-    aFixtureDef.shape = &aDynamicBox;
-    aFixtureDef.density = fDensity;
-    aFixtureDef.friction = fFriction;
-    aFixtureDef.restitution = 0.1f;
+    rtl::OUString aShapeType = rShape->getXShape()->getShapeType();
+
+    basegfx::B2DPolyPolygon aPolyPolygon;
+    // workaround:
+    // TakeXorPoly() doesn't return beziers for CustomShapes and we want the beziers
+    // so that we can decide the complexity of the polygons generated from them
+    if (aShapeType == "com.sun.star.drawing.CustomShape")
+    {
+        aPolyPolygon = static_cast<SdrObjCustomShape*>(pSdrObject)->GetLineGeometry(true);
+    }
+    else
+    {
+        aPolyPolygon = pSdrObject->TakeXorPoly();
+    }
+
+    // make beziers into polygons, using a high degree angle as fAngleBound in
+    // adaptiveSubdivideByAngle reduces complexity of the resulting polygon shapes
+    aPolyPolygon = aPolyPolygon.areControlPointsUsed()
+                       ? basegfx::utils::adaptiveSubdivideByAngle(aPolyPolygon, 20)
+                       : aPolyPolygon;
+    aPolyPolygon.removeDoublePoints();
+
+    // make polygon coordinates relative to the center of the shape instead of top left of the slide
+    aPolyPolygon
+        = basegfx::utils::distort(aPolyPolygon, aPolyPolygon.getB2DRange(),
+                                  { -aShapeBounds.getWidth() / 2, -aShapeBounds.getHeight() / 2 },
+                                  { aShapeBounds.getWidth() / 2, -aShapeBounds.getHeight() / 2 },
+                                  { -aShapeBounds.getWidth() / 2, aShapeBounds.getHeight() / 2 },
+                                  { aShapeBounds.getWidth() / 2, aShapeBounds.getHeight() / 2 });
+
+    if (pSdrObject->IsClosedObj() && !pSdrObject->IsEdgeObj() && pSdrObject->HasFillStyle())
+    {
+        basegfx::triangulator::B2DTriangleVector aTriangleVector;
+        for (auto& rPolygon : aPolyPolygon)
+        {
+            if (rPolygon.isClosed())
+            {
+                basegfx::triangulator::B2DTriangleVector aTempTriangleVector(
+                    basegfx::triangulator::triangulate(rPolygon));
+                aTriangleVector.insert(aTriangleVector.end(), aTempTriangleVector.begin(),
+                                       aTempTriangleVector.end());
+            }
+            else
+            {
+                addEdgeShapeToBody(rPolygon, pBody.get(), fDensity, fFriction, 0.1f, mfScaleFactor);
+            }
+        }
+        addTriangleVectorToBody(aTriangleVector, pBody.get(), fDensity, fFriction, 0.1f,
+                                mfScaleFactor);
+    }
+    else
+    {
+        addEdgeShapeToBody(aPolyPolygon, pBody.get(), fDensity, fFriction, 0.1f, mfScaleFactor);
+    }
 
-    pBody->CreateFixture(&aFixtureDef);
     return std::make_shared<box2DBody>(pBody, mfScaleFactor);
 }
 
diff --git a/slideshow/source/inc/box2dtools.hxx b/slideshow/source/inc/box2dtools.hxx
index dacf24845b45..7f01f09eb607 100644
--- a/slideshow/source/inc/box2dtools.hxx
+++ b/slideshow/source/inc/box2dtools.hxx
@@ -229,10 +229,9 @@ public:
      */
     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);
+    /// Create a static body from the given shape's geometry
+    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


More information about the Libreoffice-commits mailing list