Rebased ref, commits from common ancestor: commit 803c9170806f3f54a6169887b65ebc05f84b4c82 Author: Sarper Akdemir <q.sarperakde...@gmail.com> AuthorDate: Mon Jul 27 23:02:48 2020 +0300 Commit: Sarper Akdemir <q.sarperakde...@gmail.com> CommitDate: Tue Aug 4 19:12:44 2020 +0300
work-in-progress complex shapes Change-Id: I807bbde92c143b8c96792b3d8bf9603a31216486 diff --git a/slideshow/source/engine/box2dtools.cxx b/slideshow/source/engine/box2dtools.cxx index 8729300184f6..90f1d1853dba 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,124 @@ b2Vec2 convertB2DPointToBox2DVec2(const basegfx::B2DPoint& aPoint, const double return { static_cast<float>(aPoint.getX() * fScaleFactor), static_cast<float>(aPoint.getY() * -fScaleFactor) }; } + +// expects rPolygon to have coordinates relative to it's 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 a = 0; a < 3; a++) + { + for (int b = 0; b < 3; b++) + { + if (a == b) + continue; + if (b2DistanceSquared(aTriangleVertices[a], aTriangleVertices[b]) < 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); + const float fHalfWidth = 0.1; + bool bHaveEdgeA = false; + b2Vec2 aEdgeBoxVertices[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 (!bHaveEdgeA) + { + aEdgeBoxVertices[0] + = convertB2DPointToBox2DVec2(aPointA, fScaleFactor) + fHalfWidth * aEdgeNormal; + aEdgeBoxVertices[1] + = convertB2DPointToBox2DVec2(aPointA, fScaleFactor) + -fHalfWidth * aEdgeNormal; + bHaveEdgeA = true; + } + aEdgeBoxVertices[2] + = convertB2DPointToBox2DVec2(aPointB, fScaleFactor) + fHalfWidth * aEdgeNormal; + aEdgeBoxVertices[3] + = convertB2DPointToBox2DVec2(aPointB, fScaleFactor) + -fHalfWidth * aEdgeNormal; + + bool bValidPointDistance + = (b2DistanceSquared(aEdgeBoxVertices[0], aEdgeBoxVertices[2]) > 0.003f); + + if (bValidPointDistance) + { + aPolygonShape.Set(aEdgeBoxVertices, 4); + aFixture.shape = &aPolygonShape; + aFixture.density = fDensity; + aFixture.friction = fFriction; + aFixture.restitution = fRestitution; + aBody->CreateFixture(&aFixture); + + aEdgeBoxVertices[0] = aEdgeBoxVertices[2]; + aEdgeBoxVertices[1] = aEdgeBoxVertices[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 +349,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()) { @@ -352,14 +478,12 @@ Box2DBodySharedPtr box2DWorld::makeBodyStatic(const Box2DBodySharedPtr pBox2DBod 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; @@ -369,16 +493,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; + auto 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); } @@ -415,7 +586,6 @@ void box2DBody::setAngleByAngularVelocity(const double fDesiredAngle, const doub 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; diff --git a/slideshow/source/inc/box2dtools.hxx b/slideshow/source/inc/box2dtools.hxx index 0824a3c260c5..a468b886fcba 100644 --- a/slideshow/source/inc/box2dtools.hxx +++ b/slideshow/source/inc/box2dtools.hxx @@ -213,10 +213,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 _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits