blob: 88d63883e9d7e4d09db84a4b1adfd378cbe2a6fc [file] [log] [blame]
/*
* 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