RawPath::Iter improvements - Add direct accessors to RawPath::Iter for verbs and points. When not using a range-for, it's awkward to have to use "*iter" and then pick pieces out of the resulting tuple. It's also a tiny bit faster to access the type of points directly (i.e., "linePts()", "cubicPts()"), if we're already in a switch and already know the verb anyway, as opposed to having the iterator also check the verb in a generic accessor. - Update RawPath::end() to also return the correct ending pointer for the points, not just for the verbs. Diffs= 87f079a10 RawPath::Iter improvements
diff --git a/.rive_head b/.rive_head index f5c51d2..ecf6ff9 100644 --- a/.rive_head +++ b/.rive_head
@@ -1 +1 @@ -1e80ad08ff12ac794ad0abe65022ba0d10df0c5b +87f079a103eb417091b94ea5ed7eecd90cbc6149
diff --git a/include/rive/math/raw_path.hpp b/include/rive/math/raw_path.hpp index f441917..2536aad 100644 --- a/include/rive/math/raw_path.hpp +++ b/include/rive/math/raw_path.hpp
@@ -90,19 +90,58 @@ Iter() = default; Iter(const PathVerb* verbs, const Vec2D* pts) : m_verbs(verbs), m_pts(pts) {} - bool operator!=(const Iter& that) const { return m_verbs != that.m_verbs; } - bool operator==(const Iter& that) const { return m_verbs == that.m_verbs; } - - PathVerb peekVerb() const { return *m_verbs; } - - std::tuple<const PathVerb, const Vec2D* const> operator*() const + bool operator!=(const Iter& that) const { - PathVerb verb = peekVerb(); + assert(m_verbs != that.m_verbs || m_pts == that.m_pts); + return m_verbs != that.m_verbs; + } + bool operator==(const Iter& that) const + { + assert(m_verbs != that.m_verbs || m_pts == that.m_pts); + return m_verbs == that.m_verbs; + } + + // Generic accessors. The points pointer is adjusted to point to p0 for each specific verb. + PathVerb verb() const { return *m_verbs; } + const Vec2D* pts() const { return m_pts + PtsBacksetForVerb(verb()); } + std::tuple<PathVerb, const Vec2D*> operator*() const + { + PathVerb verb = *m_verbs; return {verb, m_pts + PtsBacksetForVerb(verb)}; } - Iter& operator++() - { // ++iter + // Specific point accessors for callers who already know the verb. (These may be a tiny bit + // faster in some cases since the iterator doesn't have to check the verb.) + Vec2D movePt() const + { + assert(verb() == PathVerb::move); + return m_pts[0]; + } + const Vec2D* linePts() const + { + assert(verb() == PathVerb::line); + return m_pts - 1; + } + const Vec2D* quadPts() const + { + assert(verb() == PathVerb::quad); + return m_pts - 1; + } + const Vec2D* cubicPts() const + { + assert(verb() == PathVerb::cubic); + return m_pts - 1; + } + // P0 for a close can be accessed via rawPtsPtr()[-1]. Note than p1 for a close is not in + // the array at this location. + + // Internal pointers held by the iterator. See PtsBacksetForVerb() for how pts() relates to + // the data for specific verbs. + const PathVerb* rawVerbsPtr() const { return m_verbs; } + const Vec2D* rawPtsPtr() const { return m_pts; } + + Iter& operator++() // "++iter" + { m_pts += PtsAdvanceAfterVerb(*m_verbs++); return *this; } @@ -153,7 +192,10 @@ const Vec2D* m_pts; }; Iter begin() const { return {m_Verbs.data(), m_Points.data()}; } - Iter end() const { return {m_Verbs.data() + m_Verbs.size(), nullptr}; } + Iter end() const + { + return {m_Verbs.data() + m_Verbs.size(), m_Points.data() + m_Points.size()}; + } template <typename Handler> RawPath morph(Handler proc) const {
diff --git a/test/raw_path_test.cpp b/test/raw_path_test.cpp index 815ba2e..b992f61 100644 --- a/test/raw_path_test.cpp +++ b/test/raw_path_test.cpp
@@ -91,9 +91,32 @@ std::vector<Vec2D> expectedPts) { REQUIRE(iter != end); - PathVerb verb = std::get<0>(*iter); - const Vec2D* pts = std::get<1>(*iter); + PathVerb verb = iter.verb(); + const Vec2D* pts = iter.pts(); REQUIRE(verb == expectedVerb); + switch (verb) + { + case PathVerb::move: + CHECK(expectedPts.size() == 1); + CHECK(pts[0] == iter.movePt()); + break; + case PathVerb::line: + CHECK(expectedPts.size() == 2); + CHECK(pts == iter.linePts()); + break; + case PathVerb::quad: + CHECK(expectedPts.size() == 3); + CHECK(pts == iter.quadPts()); + break; + case PathVerb::cubic: + CHECK(expectedPts.size() == 4); + CHECK(pts == iter.cubicPts()); + break; + case PathVerb::close: + CHECK(expectedPts.size() == 0); + CHECK(pts == iter.rawPtsPtr() - 1); + break; + } for (size_t i = 0; i < expectedPts.size(); ++i) { CHECK(pts[i] == expectedPts[i]);