blob: 62e6bae1a81996b04b6c80f3044fdc13afae5984 [file]
#ifndef _RIVE_PATH_COMPOSER_HPP_
#define _RIVE_PATH_COMPOSER_HPP_
#include "rive/component.hpp"
#include "rive/shapes/shape_paint_path.hpp"
#include "rive/refcnt.hpp"
#include "rive/math/raw_path.hpp"
#include "rive/shapes/path_flags.hpp"
#include <cmath>
#include <vector>
namespace rive
{
class Shape;
class CommandPath;
class PathComposer : public Component
{
public:
PathComposer(Shape* shape);
Shape* shape() const { return m_shape; }
void buildDependencies() override;
void onDirty(ComponentDirt dirt) override;
void update(ComponentDirt value) override;
ShapePaintPath* localPath() { return &m_localPath; }
ShapePaintPath* worldPath() { return &m_worldPath; }
ShapePaintPath* localClockwisePath() { return &m_localClockwisePath; }
void pathCollapseChanged();
// The inputs a local path is built from, per path, as of the last build.
struct LocalPathInput
{
Mat2D transform;
uint32_t geometryVersion;
bool skipped;
float linearTolerance;
float translationTolerance;
// The composed transform is inverseWorld * pathTransform, so it
// carries the rounding of an A^-1 * A round trip: bit equality never
// holds for a shape that has moved, even though the exact result is
// unchanged. The two halves drift at different scales, so they get
// separate tolerances, and both are compared against the transform the
// buffer was actually built from -- which is what keeps the error
// bounded by the tolerance instead of accumulating frame over frame.
bool matches(const LocalPathInput& o) const
{
if (geometryVersion != o.geometryVersion || skipped != o.skipped)
{
return false;
}
for (int i = 0; i < 4; i++)
{
if (std::abs(transform[i] - o.transform[i]) > o.linearTolerance)
{
return false;
}
}
return std::abs(transform[4] - o.transform[4]) <=
o.translationTolerance &&
std::abs(transform[5] - o.transform[5]) <=
o.translationTolerance;
}
};
bool localInputsChanged();
private:
std::vector<LocalPathInput> m_localInputs;
std::vector<LocalPathInput> m_scratchInputs;
PathFlags m_builtLocalFlags = PathFlags::none;
bool m_hasLocalInputs = false;
Shape* m_shape;
ShapePaintPath m_localPath;
ShapePaintPath m_worldPath;
ShapePaintPath m_localClockwisePath;
bool m_deferredPathDirt;
bool m_shapeNotified = false;
};
} // namespace rive
#endif