| /* |
| * 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 |