| #include "rive/animation/linear_animation.hpp" |
| #include "rive/animation/keyed_object.hpp" |
| #include "rive/artboard.hpp" |
| #include "rive/importers/artboard_importer.hpp" |
| #include "rive/importers/import_stack.hpp" |
| #include <cmath> |
| |
| using namespace rive; |
| |
| #ifdef TESTING |
| int LinearAnimation::deleteCount = 0; |
| #endif |
| |
| LinearAnimation::LinearAnimation() {} |
| |
| LinearAnimation::~LinearAnimation() |
| { |
| #ifdef TESTING |
| deleteCount++; |
| #endif |
| } |
| |
| StatusCode LinearAnimation::onAddedDirty(CoreContext* context) |
| { |
| StatusCode code; |
| for (const auto& object : m_KeyedObjects) |
| { |
| if ((code = object->onAddedDirty(context)) != StatusCode::Ok) |
| { |
| return code; |
| } |
| } |
| return StatusCode::Ok; |
| } |
| |
| StatusCode LinearAnimation::onAddedClean(CoreContext* context) |
| { |
| StatusCode code; |
| for (const auto& object : m_KeyedObjects) |
| { |
| if ((code = object->onAddedClean(context)) != StatusCode::Ok) |
| { |
| return code; |
| } |
| } |
| return StatusCode::Ok; |
| } |
| |
| void LinearAnimation::addKeyedObject(std::unique_ptr<KeyedObject> object) |
| { |
| m_KeyedObjects.push_back(std::move(object)); |
| } |
| |
| void LinearAnimation::apply(Artboard* artboard, float time, float mix) const |
| { |
| for (const auto& object : m_KeyedObjects) |
| { |
| object->apply(artboard, time, mix); |
| } |
| } |
| |
| StatusCode LinearAnimation::import(ImportStack& importStack) |
| { |
| auto artboardImporter = importStack.latest<ArtboardImporter>(ArtboardBase::typeKey); |
| if (artboardImporter == nullptr) |
| { |
| return StatusCode::MissingObject; |
| } |
| artboardImporter->addAnimation(this); |
| return Super::import(importStack); |
| } |
| |
| float LinearAnimation::startSeconds() const |
| { |
| return (enableWorkArea() ? workStart() : 0) / (float)fps(); |
| } |
| float LinearAnimation::endSeconds() const |
| { |
| return (enableWorkArea() ? workEnd() : duration()) / (float)fps(); |
| } |
| |
| float LinearAnimation::startTime() const { return (speed() >= 0) ? startSeconds() : endSeconds(); } |
| float LinearAnimation::endTime() const { return (speed() >= 0) ? endSeconds() : startSeconds(); } |
| float LinearAnimation::durationSeconds() const { return std::abs(endSeconds() - startSeconds()); } |
| |
| // Matches Dart modulus: https://api.dart.dev/stable/2.19.0/dart-core/double/operator_modulo.html |
| static float positiveMod(float value, float range) |
| { |
| assert(range > 0.0f); |
| float v = fmodf(value, range); |
| if (v < 0.0f) |
| { |
| v += range; |
| } |
| return v; |
| } |
| |
| float LinearAnimation::globalToLocalSeconds(float seconds) const |
| { |
| switch (loop()) |
| { |
| case Loop::oneShot: |
| return seconds + startTime(); |
| case Loop::loop: |
| return positiveMod(seconds, (durationSeconds())) + startTime(); |
| case Loop::pingPong: |
| float localTime = positiveMod(seconds, (durationSeconds())); |
| int direction = ((int)(seconds / (durationSeconds()))) % 2; |
| return direction == 0 ? localTime + startTime() : endTime() - localTime; |
| } |
| RIVE_UNREACHABLE(); |
| } |