blob: 086a5aa49af0156e0bce56556ff92b454968e020 [file]
#ifndef _RIVE_LINEAR_ANIMATION_INSTANCE_HPP_
#define _RIVE_LINEAR_ANIMATION_INSTANCE_HPP_
#include "rive/artboard.hpp"
#include "rive/core/field_types/core_callback_type.hpp"
#include "rive/nested_animation.hpp"
#include "rive/scene.hpp"
#include <memory>
#include <unordered_map>
namespace rive
{
class LinearAnimation;
class NestedEventNotifier;
class InterpolatingKeyFrame;
class ScriptedInterpolator;
class DataBind;
class LinearAnimationInstance : public Scene, public NestedEventNotifier
{
public:
LinearAnimationInstance(const LinearAnimation*,
ArtboardInstance*,
float speedMultiplier = 1.0);
LinearAnimationInstance(LinearAnimationInstance const&);
~LinearAnimationInstance() override;
// Advance the animation by the specified time. Returns true if the
// animation will continue to animate after this advance.
bool advance(float seconds, KeyedCallbackReporter* reporter);
bool advance(float seconds) { return advance(seconds, nullptr); }
void clearSpilledTime() { m_spilledTime = 0; }
// Returns a pointer to the instance's animation
const LinearAnimation* animation() const { return m_animation; }
// Returns the current point in time at which this instance has advance
// to
float time() const { return m_time; }
// Returns the direction that we are currently playing in
float direction() const { return m_direction; }
// Returns speed in the current direction of the animation.
float directedSpeed() const { return m_direction * speed(); }
// Update the direction of the animation instance, positive value for
// forwards Negative for backwards
void direction(int direction)
{
if (direction > 0)
{
m_direction = 1;
}
else
{
m_direction = -1;
}
}
// Sets the animation's point in time.
void time(float value);
// Applies the animation instance to its artboard instance. The mix (a value
// between 0 and 1) is the strength at which the animation is mixed with
// other animations applied to the artboard.
void apply(float mix = 1.0f) const
{
m_animation->apply(m_artboardInstance, m_time, mix, this);
}
// Returns a per-(this LAI, keyframe) stateful clone of the given
// ScriptedInterpolator, lazily creating it on first call. Returns nullptr
// when scripting is disabled or the clone cannot be vended; callers
// should fall back to the shared template (which already degrades to
// identity/linear when its Lua side isn't ready). Time-seeking via
// `time(float)` and `reset()` intentionally does NOT clear the cache —
// statefulness is the whole point.
ScriptedInterpolator* statefulInterpolator(
const InterpolatingKeyFrame* keyframe,
const ScriptedInterpolator* shared) const;
// Set when the animation is advanced, true if the animation has stopped
// (oneShot), reached the end (loop), or changed direction (pingPong)
bool didLoop() const { return m_didLoop; }
bool keepGoing() const
{
return this->loopValue() != static_cast<int>(rive::Loop::oneShot) ||
(directedSpeed() > 0 && m_time < m_animation->endSeconds()) ||
(directedSpeed() < 0 && m_time > m_animation->startSeconds());
}
bool keepGoing(float speedMultiplier) const
{
return this->loopValue() != static_cast<int>(rive::Loop::oneShot) ||
(directedSpeed() * speedMultiplier > 0 &&
m_time < m_animation->endSeconds()) ||
(directedSpeed() * speedMultiplier < 0 &&
m_time > m_animation->startSeconds());
}
float totalTime() const { return m_totalTime; }
float lastTotalTime() const { return m_lastTotalTime; }
float spilledTime() const { return m_spilledTime; }
float durationSeconds() const override;
// Forwarded from animation
uint32_t fps() const;
uint32_t duration() const;
float speed() const;
float startTime() const;
// Returns either the animation's default or overridden loop values
Loop loop() const override { return (Loop)loopValue(); }
int loopValue() const;
// Override the animation's default loop
void loopValue(int value);
bool isTranslucent() const override;
bool advanceAndApply(float seconds) override;
std::string name() const override;
void reset(float speedMultiplier);
void reportEvent(Event* event, float secondsDelay = 0.0f) override;
private:
const LinearAnimation* m_animation = nullptr;
float m_time;
float m_speedDirection;
float m_totalTime;
float m_lastTotalTime;
float m_spilledTime;
// float because it gets multiplied with other floats
float m_direction;
bool m_didLoop;
int m_loopValue = -1;
// Lazy outer pointer => the common case (no scripted interpolators) pays
// one nullptr check on the apply hot path. Inner unique_ptr destroys the
// cloned ScriptedInterpolator (and its Lua ref) when this LAI is
// destroyed. `mutable` so the cache populates from the const apply().
// Intentionally not copied by the copy ctor — a copied LAI starts with
// a fresh empty cache.
mutable std::unique_ptr<
std::unordered_map<const InterpolatingKeyFrame*,
std::unique_ptr<ScriptedInterpolator>>>
m_scriptedInterpolatorInstances;
// Data binds that cloneProperties() appended to m_artboardInstance on
// our behalf for the cloned ScriptedInterpolators above. We must
// removeDataBind+delete each of these in ~LinearAnimationInstance BEFORE
// m_scriptedInterpolatorInstances tears down, because the bind targets
// point at CustomPropertys owned by the clones. Captured by snapshotting
// m_artboardInstance->dataBinds().size() before/after each cloneScripted-
// Object call (addDataBind only ever appends). `mutable` so it can
// populate from the const apply() path. Not copied by the copy ctor.
mutable std::vector<DataBind*> m_clonedArtboardDataBinds;
};
} // namespace rive
#endif