| #include "rive/shapes/paint/trim_path.hpp" | |
| #include "rive/shapes/metrics_path.hpp" | |
| #include "rive/shapes/paint/stroke.hpp" | |
| using namespace rive; | |
| TrimPath::TrimPath() : m_TrimmedPath(makeRenderPath()) {} | |
| TrimPath::~TrimPath() { delete m_TrimmedPath; } | |
| StatusCode TrimPath::onAddedClean(CoreContext* context) { | |
| if (!parent()->is<Stroke>()) { | |
| return StatusCode::InvalidObject; | |
| } | |
| parent()->as<Stroke>()->addStrokeEffect(this); | |
| return StatusCode::Ok; | |
| } | |
| RenderPath* TrimPath::effectPath(MetricsPath* source) { | |
| if (m_RenderPath != nullptr) { | |
| return m_RenderPath; | |
| } | |
| // Source is always a containing (shape) path. | |
| const std::vector<MetricsPath*>& subPaths = source->paths(); | |
| m_TrimmedPath->reset(); | |
| auto renderOffset = std::fmod(std::fmod(offset(), 1.0f) + 1.0f, 1.0f); | |
| switch (modeValue()) { | |
| case 1: { | |
| float totalLength = source->length(); | |
| auto startLength = totalLength * (start() + renderOffset); | |
| auto endLength = totalLength * (end() + renderOffset); | |
| if (endLength < startLength) { | |
| float swap = startLength; | |
| startLength = endLength; | |
| endLength = swap; | |
| } | |
| if (startLength > totalLength) { | |
| startLength -= totalLength; | |
| endLength -= totalLength; | |
| } | |
| int i = 0, subPathCount = (int)subPaths.size(); | |
| while (endLength > 0) { | |
| auto path = subPaths[i % subPathCount]; | |
| auto pathLength = path->length(); | |
| if (startLength < pathLength) { | |
| path->trim(startLength, endLength, true, m_TrimmedPath); | |
| endLength -= pathLength; | |
| startLength = 0; | |
| } else { | |
| startLength -= pathLength; | |
| endLength -= pathLength; | |
| } | |
| i++; | |
| } | |
| } break; | |
| case 2: { | |
| for (auto path : subPaths) { | |
| auto pathLength = path->length(); | |
| auto startLength = pathLength * (start() + renderOffset); | |
| auto endLength = pathLength * (end() + renderOffset); | |
| if (endLength < startLength) { | |
| auto length = startLength; | |
| startLength = endLength; | |
| endLength = length; | |
| } | |
| if (startLength > pathLength) { | |
| startLength -= pathLength; | |
| endLength -= pathLength; | |
| } | |
| path->trim(startLength, endLength, true, m_TrimmedPath); | |
| while (endLength > pathLength) { | |
| startLength = 0; | |
| endLength -= pathLength; | |
| path->trim(startLength, endLength, true, m_TrimmedPath); | |
| } | |
| } | |
| } break; | |
| } | |
| m_RenderPath = m_TrimmedPath; | |
| return m_RenderPath; | |
| } | |
| void TrimPath::invalidateEffect() { | |
| m_RenderPath = nullptr; | |
| parent()->as<Stroke>()->parent()->addDirt(ComponentDirt::Paint); | |
| } | |
| void TrimPath::startChanged() { invalidateEffect(); } | |
| void TrimPath::endChanged() { invalidateEffect(); } | |
| void TrimPath::offsetChanged() { invalidateEffect(); } | |
| void TrimPath::modeValueChanged() { invalidateEffect(); } |