use contour_measure

Diffs=
ad4ff71df use contour_measure
diff --git a/.rive_head b/.rive_head
index a3be2d6..5e47c21 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-f9a5698be92024c4c4a1b7337575f85341d89211
+ad4ff71dfd6362883bcab2d30227d4209228f1f6
diff --git a/include/rive/math/raw_path.hpp b/include/rive/math/raw_path.hpp
index 7a89329..de32874 100644
--- a/include/rive/math/raw_path.hpp
+++ b/include/rive/math/raw_path.hpp
@@ -18,6 +18,8 @@
 
 namespace rive {
 
+class CommandPath;
+
 class RawPath {
 public:
     std::vector<Vec2D> m_Points;
@@ -130,6 +132,9 @@
         }
         return dst;
     }
+
+    // Utility for pouring a RawPath into a CommandPath
+    void addTo(CommandPath*) const;
 };
 
 } // namespace rive
diff --git a/include/rive/shapes/metrics_path.hpp b/include/rive/shapes/metrics_path.hpp
index 9748824..4ff43a7 100644
--- a/include/rive/shapes/metrics_path.hpp
+++ b/include/rive/shapes/metrics_path.hpp
@@ -2,49 +2,24 @@
 #define _RIVE_METRICS_PATH_HPP_
 
 #include "rive/command_path.hpp"
+#include "rive/math/contour_measure.hpp"
 #include "rive/math/vec2d.hpp"
 #include <cassert>
 #include <vector>
 
 namespace rive {
 
-struct CubicSegment {
-    float t;
-    float length;
-    CubicSegment(float tValue, float lengthValue) : t(tValue), length(lengthValue) {}
-};
-
-struct PathPart {
-    static const unsigned char line = 0;
-    /// Type is 0 when this is a line segment, it's 1 or greater when it's a
-    /// cubic. When it's a cubic it also represents the index in
-    /// CubicSegments-1.
-    unsigned char type;
-
-    /// Offset is the offset in original path points (which get transformed
-    /// when they're added to another path).
-    unsigned char offset;
-
-    // Only used by the cubic to count the number of cubic segments used by
-    // this part.
-    unsigned char numSegments;
-
-    PathPart(unsigned char t, unsigned char l) : type(t), offset(l), numSegments(0) {}
-};
-
 class MetricsPath : public CommandPath {
 private:
-    std::vector<Vec2D> m_Points;
-    std::vector<Vec2D> m_TransformedPoints;
-    std::vector<CubicSegment> m_CubicSegments;
-    std::vector<PathPart> m_Parts;
-    std::vector<float> m_Lengths;
+    RawPath m_RawPath; // temporary, until we build m_Contour
+    rcp<ContourMeasure> m_Contour;
     std::vector<MetricsPath*> m_Paths;
-    float m_ComputedLength = 0.0f;
     Mat2D m_ComputedLengthTransform;
+    float m_ComputedLength = 0;
 
 public:
     const std::vector<MetricsPath*>& paths() const { return m_Paths; }
+
     void addPath(CommandPath* path, const Mat2D& transform) override;
     void reset() override;
     void moveTo(float x, float y) override;
@@ -61,10 +36,6 @@
 
 private:
     float computeLength(const Mat2D& transform);
-
-    /// Extract a single segment from startT to endT as render commands
-    /// added to result.
-    void extractSubPart(int index, float startT, float endT, bool moveTo, RenderPath* result);
 };
 
 class OnlyMetricsPath : public MetricsPath {
diff --git a/src/math/raw_path.cpp b/src/math/raw_path.cpp
index 5f45a8c..fd4419a 100644
--- a/src/math/raw_path.cpp
+++ b/src/math/raw_path.cpp
@@ -236,4 +236,21 @@
 void RawPath::rewind() {
     m_Points.clear();
     m_Verbs.clear();
-}
\ No newline at end of file
+}
+
+///////////////////////////////////
+
+#include "rive/command_path.hpp"
+
+void RawPath::addTo(CommandPath* result) const {
+    RawPath::Iter iter(*this);
+    while (auto rec = iter.next()) {
+        switch (rec.verb) {
+            case PathVerb::move: result->move(rec.pts[0]); break;
+            case PathVerb::line: result->line(rec.pts[0]); break;
+            case PathVerb::quad: assert(false); break;
+            case PathVerb::cubic: result->cubic(rec.pts[0], rec.pts[1], rec.pts[2]); break;
+            case PathVerb::close: result->close(); break;
+        }
+    }
+}
diff --git a/src/shapes/metrics_path.cpp b/src/shapes/metrics_path.cpp
index 38f77a9..5082f6d 100644
--- a/src/shapes/metrics_path.cpp
+++ b/src/shapes/metrics_path.cpp
@@ -2,21 +2,17 @@
 #include "rive/shapes/metrics_path.hpp"
 #include "rive/renderer.hpp"
 #include "rive/math/cubic_utilities.hpp"
+#include "rive/math/raw_path.hpp"
+#include "rive/math/contour_measure.hpp"
 
 using namespace rive;
 
-static float clamp(float v, float lo, float hi) { return std::min(std::max(v, lo), hi); }
-
-// Less exact, but faster, than std::lerp
-static float lerp(float from, float to, float f) { return from + f * (to - from); }
-
 void MetricsPath::reset() {
-    m_ComputedLength = 0.0f;
-    m_CubicSegments.clear();
-    m_Points.clear();
-    m_Parts.clear();
-    m_Lengths.clear();
     m_Paths.clear();
+    m_Contour.reset(nullptr);
+    m_RawPath = RawPath();
+    m_ComputedLengthTransform = Mat2D();
+    m_ComputedLength = 0;
 }
 
 void MetricsPath::addPath(CommandPath* path, const Mat2D& transform) {
@@ -26,122 +22,28 @@
 }
 
 void MetricsPath::moveTo(float x, float y) {
-    assert(m_Points.size() == 0);
-    m_Points.emplace_back(Vec2D(x, y));
+    assert(m_RawPath.points().size() == 0);
+    m_RawPath.move({x, y});
 }
 
-void MetricsPath::lineTo(float x, float y) {
-    // TODO: resize PathPart to allow for larger offsets
-    auto offset = castTo<uint8_t>(m_Points.size());
-    m_Parts.push_back(PathPart(0, offset));
-    m_Points.emplace_back(Vec2D(x, y));
-}
+void MetricsPath::lineTo(float x, float y) { m_RawPath.line({x, y}); }
 
 void MetricsPath::cubicTo(float ox, float oy, float ix, float iy, float x, float y) {
-    auto offset = castTo<uint8_t>(m_Points.size());
-    m_Parts.push_back(PathPart(1, offset));
-    m_Points.emplace_back(Vec2D(ox, oy));
-    m_Points.emplace_back(Vec2D(ix, iy));
-    m_Points.emplace_back(Vec2D(x, y));
+    m_RawPath.cubic({ox, oy}, {ix, iy}, {x, y});
 }
 
-void MetricsPath::close() {}
-
-static const float minSegmentLength = 0.05f;
-static const float distTooFar = 1.0f;
-
-static float segmentCubic(const Vec2D& from,
-                          const Vec2D& fromOut,
-                          const Vec2D& toIn,
-                          const Vec2D& to,
-                          float runningLength,
-                          float t1,
-                          float t2,
-                          std::vector<CubicSegment>& segments) {
-
-    if (CubicUtilities::shouldSplitCubic(from, fromOut, toIn, to, distTooFar)) {
-        float halfT = (t1 + t2) / 2.0f;
-
-        Vec2D hull[6];
-        CubicUtilities::computeHull(from, fromOut, toIn, to, 0.5f, hull);
-
-        runningLength =
-            segmentCubic(from, hull[0], hull[3], hull[5], runningLength, t1, halfT, segments);
-        runningLength =
-            segmentCubic(hull[5], hull[4], hull[2], to, runningLength, halfT, t2, segments);
-    } else {
-        float length = Vec2D::distance(from, to);
-        runningLength += length;
-        if (length > minSegmentLength) {
-            segments.emplace_back(CubicSegment(t2, runningLength));
-        }
-    }
-    return runningLength;
+void MetricsPath::close() {
+    // Should we pass the close() to our m_RawPath ???
 }
 
 float MetricsPath::computeLength(const Mat2D& transform) {
-    // If the pre-computed length is still valid (transformed with the same
-    // transform) just return that.
-    if (!m_Lengths.empty() && transform == m_ComputedLengthTransform) {
-        return m_ComputedLength;
+    // Only compute if our pre-computed length is not valid
+    if (!m_Contour || transform != m_ComputedLengthTransform) {
+        m_ComputedLengthTransform = transform;
+        m_Contour = ContourMeasureIter(m_RawPath * transform).next();
+        m_ComputedLength = m_Contour ? m_Contour->length() : 0;
     }
-    m_ComputedLengthTransform = transform;
-    m_Lengths.clear();
-    m_CubicSegments.clear();
-
-    // We have to dupe the transformed points as we're not sure whether just the
-    // transform is changing (path may not have been reset but got added with
-    // another transform).
-    m_TransformedPoints.resize(m_Points.size());
-    for (size_t i = 0, l = m_Points.size(); i < l; i++) {
-        m_TransformedPoints[i] = transform * m_Points[i];
-    }
-
-    // Should never have subPaths with more subPaths (Skia allows this but for
-    // Rive this isn't necessary and it keeps things simpler).
-    assert(m_Paths.empty());
-    const Vec2D* pen = &m_TransformedPoints[0];
-    int idx = 1;
-    float length = 0.0f;
-
-    for (PathPart& part : m_Parts) {
-        switch (part.type) {
-            case PathPart::line: {
-                const Vec2D& point = m_TransformedPoints[idx++];
-
-                float partLength = Vec2D::distance(*pen, point);
-                m_Lengths.push_back(partLength);
-                pen = &point;
-                length += partLength;
-                break;
-            }
-            // Anything above 0 is the number of cubic parts...
-            default: {
-                // Subdivide as necessary...
-
-                // push in the parts
-
-                const Vec2D& from = pen[0];
-                const Vec2D& fromOut = pen[1];
-                const Vec2D& toIn = pen[2];
-                const Vec2D& to = pen[3];
-
-                idx += 3;
-                pen = &to;
-
-                int index = (int)m_CubicSegments.size();
-                part.type = castTo<uint8_t>(index + 1);
-                float partLength =
-                    segmentCubic(from, fromOut, toIn, to, 0.0f, 0.0f, 1.0f, m_CubicSegments);
-                m_Lengths.push_back(partLength);
-                length += partLength;
-                part.numSegments = castTo<uint8_t>(m_CubicSegments.size() - index);
-                break;
-            }
-        }
-    }
-    m_ComputedLength = length;
-    return length;
+    return m_ComputedLength;
 }
 
 void MetricsPath::trim(float startLength, float endLength, bool moveTo, RenderPath* result) {
@@ -150,179 +52,13 @@
         m_Paths.front()->trim(startLength, endLength, moveTo, result);
         return;
     }
-    if (startLength == endLength) {
-        // nothing to trim.
-        return;
-    }
-    // We need to find the first part to trim.
-    float length = 0.0f;
 
-    int partCount = (int)m_Parts.size();
-    int firstPartIndex = -1, lastPartIndex = partCount - 1;
-    float startT = 0.0f, endT = 1.0f;
-    // Find first part.
-    for (int i = 0; i < partCount; i++) {
-        float partLength = m_Lengths[i];
-        if (length + partLength > startLength) {
-            firstPartIndex = i;
-            startT = (startLength - length) / partLength;
-            break;
-        }
-        length += partLength;
-    }
-    if (firstPartIndex == -1) {
-        // Couldn't find it.
-        return;
-    }
-
-    // Find last part.
-    for (int i = firstPartIndex; i < partCount; i++) {
-        float partLength = m_Lengths[i];
-        if (length + partLength >= endLength) {
-            lastPartIndex = i;
-            endT = (endLength - length) / partLength;
-            break;
-        }
-        length += partLength;
-    }
-
-    // Lets make sur we're between 0 & 1f on both start & end.
-    startT = clamp(startT, 0.0f, 1.0f);
-    endT = clamp(endT, 0.0f, 1.0f);
-
-    if (firstPartIndex == lastPartIndex) {
-        extractSubPart(firstPartIndex, startT, endT, moveTo, result);
-    } else {
-        extractSubPart(firstPartIndex, startT, 1.0f, moveTo, result);
-        for (int i = firstPartIndex + 1; i < lastPartIndex; i++) {
-            // add entire part...
-            const PathPart& part = m_Parts[i];
-            switch (part.type) {
-                case PathPart::line: {
-                    result->line(m_TransformedPoints[part.offset]);
-                    break;
-                }
-                default: {
-                    result->cubic(m_TransformedPoints[part.offset + 0],
-                                  m_TransformedPoints[part.offset + 1],
-                                  m_TransformedPoints[part.offset + 2]);
-                    break;
-                }
-            }
-        }
-        extractSubPart(lastPartIndex, 0.0f, endT, false, result);
-    }
-}
-
-void MetricsPath::extractSubPart(int index,
-                                 float startT,
-                                 float endT,
-                                 bool moveTo,
-                                 RenderPath* result) {
-    assert(startT >= 0.0f && startT <= 1.0f && endT >= 0.0f && endT <= 1.0f);
-    const PathPart& part = m_Parts[index];
-    switch (part.type) {
-        case PathPart::line: {
-            const Vec2D from = m_TransformedPoints[part.offset - 1];
-            const Vec2D to = m_TransformedPoints[part.offset];
-            const Vec2D dir = to - from;
-            if (moveTo) {
-                result->move(from + dir * startT);
-            }
-            result->line(from + dir * endT);
-
-            break;
-        }
-        default: {
-            auto startingSegmentIndex = part.type - 1;
-            auto startEndSegmentIndex = startingSegmentIndex;
-            auto endingSegmentIndex = startingSegmentIndex + part.numSegments;
-
-            // Find cubicStartT and cubicEndT
-            float length = m_Lengths[index];
-            if (startT != 0.0f) {
-                float startLength = startT * length;
-                for (int si = startingSegmentIndex; si < endingSegmentIndex; si++) {
-                    const CubicSegment& segment = m_CubicSegments[si];
-                    if (segment.length >= startLength) {
-                        if (si == startingSegmentIndex) {
-                            startT = segment.t * (startLength / segment.length);
-                        } else {
-                            float previousLength = m_CubicSegments[si - 1].length;
-
-                            float t =
-                                (startLength - previousLength) / (segment.length - previousLength);
-                            startT = lerp(m_CubicSegments[si - 1].t, segment.t, t);
-                        }
-                        // Help out the ending segment finder by setting its
-                        // start to where we landed while finding the first
-                        // segment, that way it can skip a bunch of work.
-                        startEndSegmentIndex = si;
-                        break;
-                    }
-                }
-            }
-
-            if (endT != 1.0f) {
-                float endLength = endT * length;
-                for (int si = startEndSegmentIndex; si < endingSegmentIndex; si++) {
-                    const CubicSegment& segment = m_CubicSegments[si];
-                    if (segment.length >= endLength) {
-                        if (si == startingSegmentIndex) {
-                            endT = segment.t * (endLength / segment.length);
-                        } else {
-                            float previousLength = m_CubicSegments[si - 1].length;
-
-                            float t =
-                                (endLength - previousLength) / (segment.length - previousLength);
-                            endT = lerp(m_CubicSegments[si - 1].t, segment.t, t);
-                        }
-                        break;
-                    }
-                }
-            }
-
-            Vec2D hull[6];
-
-            const Vec2D& from = m_TransformedPoints[part.offset - 1];
-            const Vec2D& fromOut = m_TransformedPoints[part.offset];
-            const Vec2D& toIn = m_TransformedPoints[part.offset + 1];
-            const Vec2D& to = m_TransformedPoints[part.offset + 2];
-
-            if (startT == 0.0f) {
-                // Start is 0, so split at end and keep the left side.
-                CubicUtilities::computeHull(from, fromOut, toIn, to, endT, hull);
-                if (moveTo) {
-                    result->move(from);
-                }
-                result->cubic(hull[0], hull[3], hull[5]);
-            } else {
-                // Split at start since it's non 0.
-                CubicUtilities::computeHull(from, fromOut, toIn, to, startT, hull);
-                if (moveTo) {
-                    // Move to first point on the right side.
-                    result->move(hull[5]);
-                }
-                if (endT == 1.0f) {
-                    // End is 1, so no further split is necessary just cubicTo
-                    // the remaining right side.
-                    result->cubic(hull[4], hull[2], to);
-                } else {
-                    // End is not 1, so split again and cubic to the left side
-                    // of the split and remap endT to the new curve range
-                    CubicUtilities::computeHull(hull[5],
-                                                hull[4],
-                                                hull[2],
-                                                to,
-                                                (endT - startT) / (1.0f - startT),
-                                                hull);
-
-                    result->cubic(hull[0], hull[3], hull[5]);
-                }
-            }
-            break;
-        }
-    }
+    // TODO: if we can change the signature of MetricsPath and/or trim() to speak native
+    //       rawpaths, we wouldn't need this temporary copy (since ContourMeasure speaks
+    //       native rawpaths).
+    RawPath tmp;
+    m_Contour->getSegment(startLength, endLength, &tmp, moveTo);
+    tmp.addTo(result);
 }
 
 RenderMetricsPath::RenderMetricsPath(std::unique_ptr<RenderPath> path) :