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]);