blob: b02d5241bbf8ace9955d6368b985ddc8a3d8d607 [file] [log] [blame]
/*
* Copyright 2022 Rive
*/
#ifndef _RIVE_RAW_PATH_HPP_
#define _RIVE_RAW_PATH_HPP_
#include "rive/span.hpp"
#include "rive/math/aabb.hpp"
#include "rive/math/mat2d.hpp"
#include "rive/math/path_types.hpp"
#include "rive/math/vec2d.hpp"
#include <cmath>
#include <stdio.h>
#include <cstdint>
#include <vector>
namespace rive {
class RawPath {
public:
std::vector<Vec2D> m_Points;
std::vector<PathVerb> m_Verbs;
// Construct a RawPath from count points and verbs.
RawPath(const Vec2D* points,
std::size_t pointCount,
const PathVerb* verbs,
std::size_t verbCount);
RawPath() {}
~RawPath() {}
bool empty() const { return m_Points.empty(); }
AABB bounds() const;
void move(Vec2D);
void line(Vec2D);
void quad(Vec2D, Vec2D);
void cubic(Vec2D, Vec2D, Vec2D);
void close();
// Makes the path empty and frees any memory allocated by the drawing
// (line, curve, move, close) calls.
void reset();
// Makes the path empty but keeps the memory for the drawing calls reserved.
void rewind();
RawPath transform(const Mat2D&) const;
void transformInPlace(const Mat2D&);
RawPath operator*(const Mat2D& mat) const { return this->transform(mat); }
Span<const Vec2D> points() const { return toSpan(m_Points); }
Span<Vec2D> points() { return toSpan(m_Points); }
Span<const PathVerb> verbs() const { return toSpan(m_Verbs); }
Span<PathVerb> verbs() { return toSpan(m_Verbs); }
Span<const uint8_t> verbsU8() const {
const uint8_t* ptr = (const uint8_t*)m_Verbs.data();
return Span<const uint8_t>(ptr, m_Verbs.size());
}
// Syntactic sugar for x,y -vs- vec2d
void moveTo(float x, float y) { move({x, y}); }
void lineTo(float x, float y) { line({x, y}); }
void quadTo(float x, float y, float x1, float y1) { quad({x, y}, {x1, y1}); }
void cubicTo(float x, float y, float x1, float y1, float x2, float y2) {
cubic({x, y}, {x1, y1}, {x2, y2});
}
// Helpers for adding new contours
void addRect(const AABB&, PathDirection = PathDirection::cw);
void addOval(const AABB&, PathDirection = PathDirection::cw);
void addPoly(Span<const Vec2D>, bool isClosed);
class Iter {
const Vec2D* m_currPts;
const PathVerb* m_currVerb;
const PathVerb* m_stopVerb; // 1 past last verb
public:
Iter() : m_currPts(nullptr), m_currVerb(nullptr), m_stopVerb(nullptr) {}
Iter(const RawPath& path) { this->reset(path); }
void reset(const RawPath& path) {
m_currPts = path.m_Points.data();
m_currVerb = path.m_Verbs.data();
m_stopVerb = path.m_Verbs.data() + path.m_Verbs.size();
}
// returns true iff next() will return false
bool isDone() const { return m_currVerb >= m_stopVerb; }
struct Rec {
const Vec2D* pts;
int count;
PathVerb verb;
operator bool() const { return pts != nullptr; }
};
Rec next();
void backUp();
};
template <typename Handler> RawPath morph(Handler proc) const {
RawPath dst;
// todo: dst.reserve(src.ptCount, src.verbCount);
RawPath::Iter iter(*this);
while (auto rec = iter.next()) {
Vec2D pts[3];
for (int i = 0; i < rec.count; ++i) {
pts[i] = proc(rec.pts[i]);
}
switch (rec.verb) {
case PathVerb::move:
dst.move(pts[0]);
break;
case PathVerb::line:
dst.line(pts[0]);
break;
case PathVerb::quad:
dst.quad(pts[0], pts[1]);
break;
case PathVerb::cubic:
dst.cubic(pts[0], pts[1], pts[2]);
break;
case PathVerb::close:
dst.close();
break;
}
}
return dst;
}
};
} // namespace rive
#endif