Fix follow path contention with MetricsPath Building contour measures on the follow path constraint to remove contention on stored contour measures in the MetricsPath. We were storing the contour data used for measuring on the MetricsPath. Calling computeLength with a different transform would mutate that stored contour data. So having the trim path and the FollowPathConstraint call computeLength with different transforms was causing problems as neither of them was guaranteed the contour data was for the correct transform they expected. I fixed this by having the FollowPathConstraint store its own contours for the path. Diffs= 033489f8b Fix follow path contention with MetricsPath (#5868) Co-authored-by: Luigi Rosso <luigi-rosso@users.noreply.github.com>
diff --git a/.rive_head b/.rive_head index bb2edbd..ecde75e 100644 --- a/.rive_head +++ b/.rive_head
@@ -1 +1 @@ -ad4236501bdf0218046a93c54f2ee4a379747890 +033489f8bb031318adc12cd418dde42e89635061
diff --git a/include/rive/constraints/follow_path_constraint.hpp b/include/rive/constraints/follow_path_constraint.hpp index 391d1f9..604e578 100644 --- a/include/rive/constraints/follow_path_constraint.hpp +++ b/include/rive/constraints/follow_path_constraint.hpp
@@ -8,10 +8,6 @@ { class FollowPathConstraint : public FollowPathConstraintBase { -private: - std::unique_ptr<MetricsPath> m_WorldPath; - TransformComponents m_ComponentsA; - TransformComponents m_ComponentsB; public: void distanceChanged() override; @@ -21,6 +17,12 @@ void constrain(TransformComponent* component) override; void update(ComponentDirt value) override; void buildDependencies() override; + +private: + RawPath m_rawPath; + std::vector<rcp<ContourMeasure>> m_contours; + TransformComponents m_ComponentsA; + TransformComponents m_ComponentsB; }; } // namespace rive
diff --git a/include/rive/shapes/metrics_path.hpp b/include/rive/shapes/metrics_path.hpp index 2c1e7f7..6f104e4 100644 --- a/include/rive/shapes/metrics_path.hpp +++ b/include/rive/shapes/metrics_path.hpp
@@ -37,6 +37,9 @@ /// computeLength be called prior to trimming. void trim(float startLength, float endLength, bool moveTo, RenderPath* result); + /// Add this MetricsPath to a raw path with a transform. + RawPath::Iter addToRawPath(RawPath& rawPath, const Mat2D& transform) const; + private: float computeLength(const Mat2D& transform); };
diff --git a/src/constraints/follow_path_constraint.cpp b/src/constraints/follow_path_constraint.cpp index af2664a..55c54f7 100644 --- a/src/constraints/follow_path_constraint.cpp +++ b/src/constraints/follow_path_constraint.cpp
@@ -35,24 +35,23 @@ { return m_Target->worldTransform(); } - MetricsPath* metricsPath = m_WorldPath.get(); - if (metricsPath == nullptr) + + float totalLength = 0.0f; + for (auto contour : m_contours) { - return m_Target->worldTransform(); + totalLength += contour->length(); } - const std::vector<MetricsPath*>& paths = metricsPath->paths(); - float totalLength = metricsPath->length(); float actualDistance = positiveMod(distance(), 1.0f); float distanceUnits = totalLength * std::min(1.0f, std::max(0.0f, actualDistance)); float runningLength = 0; ContourMeasure::PosTan posTan; - for (auto path : paths) + for (auto contour : m_contours) { - float pathLength = path->length(); + float pathLength = contour->length(); if (distanceUnits < pathLength + runningLength) { - posTan = path->contourMeasure()->getPosTan(distanceUnits - runningLength); + posTan = contour->getPosTan(distanceUnits - runningLength); break; } runningLength += pathLength; @@ -85,7 +84,6 @@ { return; } - const Mat2D& transformA = component->worldTransform(); Mat2D transformB(targetTransform()); if (sourceSpace() == TransformSpace::local) @@ -135,18 +133,18 @@ Shape* shape = static_cast<Shape*>(m_Target); if (hasDirt(value, ComponentDirt::Path)) { - if (m_WorldPath == nullptr) - { - m_WorldPath = std::unique_ptr<MetricsPath>(new OnlyMetricsPath()); - } - else - { - m_WorldPath->rewind(); - } + m_rawPath.rewind(); + m_contours.clear(); for (auto path : shape->paths()) { - const Mat2D& transform = path->pathTransform(); - m_WorldPath->addPath(path->commandPath(), transform); + auto commandPath = static_cast<MetricsPath*>(path->commandPath()); + commandPath->addToRawPath(m_rawPath, path->pathTransform()); + } + + auto measure = ContourMeasureIter(m_rawPath); + for (auto contour = measure.next(); contour != nullptr; contour = measure.next()) + { + m_contours.push_back(contour); } } }
diff --git a/src/shapes/metrics_path.cpp b/src/shapes/metrics_path.cpp index 8031a6d..be8ce44 100644 --- a/src/shapes/metrics_path.cpp +++ b/src/shapes/metrics_path.cpp
@@ -23,6 +23,11 @@ m_Paths.emplace_back(metricsPath); } +RawPath::Iter MetricsPath::addToRawPath(RawPath& rawPath, const Mat2D& transform) const +{ + return rawPath.addPath(m_RawPath, &transform); +} + void MetricsPath::moveTo(float x, float y) { assert(m_RawPath.points().size() == 0);