blob: ea521ec18ec759fcac1cbc23adf3bc8dac580889 [file] [log] [blame] [edit]
#include "rive/math/path_measure.hpp"
#include <algorithm>
using namespace rive;
PathMeasure::PathMeasure() : m_length(0.0f) {}
PathMeasure::PathMeasure(const RawPath* path, float tol) : m_length(0.0f)
{
auto measure = ContourMeasureIter(path, tol);
for (auto contour = measure.next(); contour != nullptr;
contour = measure.next())
{
m_length += contour->length();
m_contours.push_back(contour);
}
}
ContourMeasure::PosTanDistance PathMeasure::atDistance(float distance) const
{
float currentDistance = distance;
for (auto contour : m_contours)
{
float contourLength = contour->length();
if (currentDistance - contourLength <= 0)
{
return ContourMeasure::PosTanDistance(
contour->getPosTan(currentDistance),
distance);
}
currentDistance -= contourLength;
}
return ContourMeasure::PosTanDistance();
}
ContourMeasure::PosTanDistance PathMeasure::atPercentage(
float percentageDistance) const
{
float inRangePercentage = fmodf(percentageDistance, 1.0f);
if (inRangePercentage < 0.0f)
{
inRangePercentage += 1.0f;
}
// Mod to correct percentage (0-100%) and make sure we actually reach
// 100%.
if (percentageDistance != 0.0f && inRangePercentage == 0.0f)
{
inRangePercentage = 1.0f;
}
return atDistance(m_length * inRangePercentage);
}
void PathMeasure::getSegment(float startDistance,
float endDistance,
RawPath* dst,
bool startWithMove) const
{
if (dst == nullptr || m_contours.empty())
{
return;
}
// Clamp distances to valid range
startDistance = std::max(0.0f, std::min(startDistance, m_length));
endDistance = std::max(0.0f, std::min(endDistance, m_length));
if (startDistance >= endDistance)
{
return;
}
float currentDistance = 0.0f;
bool isFirstSegment = true;
for (auto contour : m_contours)
{
float contourLength = contour->length();
float contourStart = currentDistance;
float contourEnd = currentDistance + contourLength;
// Check if this contour intersects with the requested range
if (contourEnd > startDistance && contourStart < endDistance)
{
// Calculate the local distances within this contour
float localStart = std::max(0.0f, startDistance - contourStart);
float localEnd =
std::min(contourLength, endDistance - contourStart);
// Extract from this contour
contour->getSegment(localStart,
localEnd,
dst,
!isFirstSegment || startWithMove);
isFirstSegment = false;
}
currentDistance += contourLength;
// If we've passed the end distance, we're done
if (currentDistance >= endDistance)
{
break;
}
}
}
bool PathMeasure::isClosed() const
{
// Return true only if there is exactly one contour and it is closed
return m_contours.size() == 1 && m_contours[0]->isClosed();
}