| /* | |
| * Copyright 2022 Rive | |
| */ | |
| #ifndef _RIVE_CONTOUR_MEASURE_HPP_ | |
| #define _RIVE_CONTOUR_MEASURE_HPP_ | |
| #include "rive/math/raw_path.hpp" | |
| #include "rive/math/vec2d.hpp" | |
| #include "rive/refcnt.hpp" | |
| #include <utility> | |
| namespace rive | |
| { | |
| class ContourMeasure : public RefCnt<ContourMeasure> | |
| { | |
| public: | |
| static constexpr unsigned kMaxDot30 = (1 << 30) - 1; | |
| static constexpr float kInvScaleD30 = 1.0f / (float)kMaxDot30; | |
| // Deliberately making this pack well (12 bytes) | |
| struct Segment | |
| { | |
| float m_distance; // total distance up to this point | |
| uint32_t m_ptIndex; // index of the first point for this line/quad/cubic | |
| unsigned m_tValue : 30; // Dot30 t value for the end of this segment | |
| unsigned m_type : 2; // [private enum] | |
| float getT() const { return m_tValue * kInvScaleD30; } | |
| bool operator<(const Segment& other) const { return m_distance < other.m_distance; } | |
| void extract(RawPath* dst, float fromT, float toT, const Vec2D pts[], bool moveTo) const; | |
| void extract(RawPath* dst, const Vec2D pts[]) const; | |
| }; | |
| private: | |
| size_t findSegment(float distance) const; | |
| std::vector<Segment> m_segments; | |
| std::vector<Vec2D> m_points; | |
| const float m_length; | |
| const bool m_isClosed; | |
| ContourMeasure(std::vector<Segment>&&, std::vector<Vec2D>&&, float length, bool isClosed); | |
| friend class ContourMeasureIter; | |
| public: | |
| float length() const { return m_length; } | |
| bool isClosed() const { return m_isClosed; } | |
| struct PosTan | |
| { | |
| Vec2D pos, tan; | |
| }; | |
| PosTan getPosTan(float distance) const; | |
| void getSegment(float startDistance, float endDistance, RawPath* dst, bool startWithMove) const; | |
| Vec2D warp(Vec2D src) const | |
| { | |
| const auto result = this->getPosTan(src.x); | |
| return { | |
| result.pos.x - result.tan.y * src.y, | |
| result.pos.y + result.tan.x * src.y, | |
| }; | |
| } | |
| void dump() const; | |
| }; | |
| class ContourMeasureIter | |
| { | |
| RawPath m_optionalCopy; | |
| RawPath::Iter m_iter; | |
| RawPath::Iter m_end; | |
| const Vec2D* m_srcPoints; | |
| float m_invTolerance; | |
| float addQuadSegs(std::vector<ContourMeasure::Segment>&, | |
| const Vec2D[], | |
| uint32_t ptIndex, | |
| float distance) const; | |
| float addCubicSegs(std::vector<ContourMeasure::Segment>&, | |
| const Vec2D[], | |
| uint32_t ptIndex, | |
| float distance) const; | |
| rcp<ContourMeasure> tryNext(); | |
| public: | |
| // Tolerance is the max deviation of the curve from its approximating line | |
| // segments. A smaller tolerance means more line segments, but a better | |
| // approximation for the curves actual length. | |
| static constexpr float kDefaultTolerance = 0.5f; | |
| ContourMeasureIter(const RawPath& path, float tol = kDefaultTolerance) | |
| { | |
| this->reset(path, tol); | |
| } | |
| void reset(const RawPath&, float = kDefaultTolerance); | |
| // Returns a measure object for each contour in the path | |
| // (contours with zero-length are skipped over) | |
| // and then returns nullptr when its finished. | |
| // | |
| // ContourMeasureIter iter(path); | |
| // while ((auto meas = iter.next())) { | |
| // ... meas can be used, and passed to other objects | |
| // } | |
| // | |
| // Each measure object is stand-alone, and can outlive the ContourMeasureIter | |
| // that created it. It contains no back pointers to the Iter or to the path. | |
| // | |
| rcp<ContourMeasure> next(); | |
| }; | |
| } // namespace rive | |
| #endif |