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

Sarper Akdemir (via logerrit) logerrit at kemper.freedesktop.org
Thu Aug 20 13:23:38 UTC 2020


 slideshow/source/engine/box2dtools.cxx |  209 ++++++++++++++++++++++++++++++---
 slideshow/source/inc/box2dtools.hxx    |    7 -
 2 files changed, 198 insertions(+), 18 deletions(-)

New commits:
commit 51275a633ff5f8aa5843ba1622c701fd7fdda5a0
Author:     Sarper Akdemir <q.sarperakdemir at gmail.com>
AuthorDate: Mon Jul 27 23:02:48 2020 +0300
Commit:     Thorsten Behrens <Thorsten.Behrens at CIB.de>
CommitDate: Thu Aug 20 15:23:01 2020 +0200

    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
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/100432
    Tested-by: Jenkins
    Reviewed-by: Thorsten Behrens <Thorsten.Behrens at CIB.de>

diff --git a/slideshow/source/engine/box2dtools.cxx b/slideshow/source/engine/box2dtools.cxx
index c02a967a5f87..6fa427c15c9a 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,134 @@ 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)
+{
+    // make sure there's no bezier curves on the polygon
+    assert(!rPolygon.areControlPointsUsed());
+    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.1f;
+    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 (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 +359,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())
             {
@@ -392,14 +528,12 @@ Box2DBodySharedPtr makeBodyStatic(const Box2DBodySharedPtr& pBox2DBody)
     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;
@@ -409,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 77543ea919e1..474f01c60a63 100644
--- a/slideshow/source/inc/box2dtools.hxx
+++ b/slideshow/source/inc/box2dtools.hxx
@@ -211,10 +211,9 @@ public:
      */
     Box2DBodySharedPtr makeShapeStatic(const slideshow::internal::ShapeSharedPtr& pShape);
 
-    /// 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