tldr:
- Added support for -hopefully- all of the shapes in LibreOffice
- Static body creation now only uses SdrObjects and has a more coherent implementation.
- Shapes that mix lines and polygons/beziers are also supported.
If you’re interested in what I’m working on you can check out my public wekan board
Last week I’ve started by working on support for Custom Shapes. At first I didn’t how could I get the related geometry information about Custom Shapes. Upon asking on IRC, mst (Michael Stahl) directed me to SdrObject class. Inspecting this class, found out a child of it that handles Custom Shapes called SdrObjCustomShape had a function SdrObjCustomShape::GetLineGeometry was returning exactly what I’ve wanted in the first place a B2DPolyPolygon that represents the shape! So I went ahead and created an implementation that if the shape type is CustomShape, it got corresponding SdrObject using it’s XShape and casted the SdrObject* to an SdrObjCustomShape* and got the B2DPolyPolygon from that. Then it triangulated this polygon using basegfx::triangulator::triangulate, and added resulting collection of triangles to a box2d body.
Later, since achieving such a result using an SdrObject was quite easy and took care of rotations and skews applied to the shape with ease, I wanted to drop my prior implementations for ClosedBezierShape and ClosedPolygonShape and use SdrObject for everything if possible. To make this happen, went back and inspected SdrObject again realizing SdrObjCustomShape::TakeXorPolygon used SdrObjCustomShape::GetLineGeometry. TakeXorPolygon according to it’s documentation was used for dragging objects but suited my case perfectly since it was returning B2DPolyPolygon representation for the corresponding shape. So I proceeded by using that for every type of shape and started to build a generalized implementation that wasn’t specific to shape type.
While doing so I’ve added support for shapes that were open, non-filled polygons or merely edges. Box2D required dynamic shapes to have an actual volume to properly simulate dynamic bodies. So I’ve set an arbitary width value and used it to offset copies of edge start and end points parallel to the edge normal, on opposite directions to each other giving them some volume. Looped through the whole B2DPolyPolygon constructing quadrilaterals that reperesented each edge and added these to box2d body.
Lastly, bezier curves that were triangulated resulted in a big performance hit since some of the custom shapes ended up having ~800 triangles, this amount of precision was mostly useless. To resolve this problem before doing anything with B2DPolyPolygons I’ve subdivided bezier shapes with basegfx::utils::adaptiveSubdivideByAngle giving it a fAngleBound of 20 degrees. Even though this is a pretty high angle and at first i thought it would end up making shapes look clunky when animated. I believe this isn’t the case since there are high amount of control points to begin with and fAngleBound only applies between two control points in a bezier curve.
Here are some visualizations of how some of the shapes are represented in the box2d world right now: