blob: 6704da19515d2e5e3785d37be949772c304184d8 [file]
#ifndef _RIVE_LINEAR_ANIMATION_HPP_
#define _RIVE_LINEAR_ANIMATION_HPP_
#include "rive/animation/loop.hpp"
#include "rive/generated/animation/linear_animation_base.hpp"
#include <vector>
namespace rive
{
class Artboard;
class KeyedObject;
class KeyedCallbackReporter;
class LinearAnimationInstance;
class LinearAnimation : public LinearAnimationBase
{
private:
std::vector<std::unique_ptr<KeyedObject>> m_KeyedObjects;
#ifdef WITH_RIVE_EDITOR
// Non-owning parallel list populated by `EditorFile::finalizeBatch`
// from coop-hydrated KeyedObjects whose `animationId` (an editor-
// only `runtime: false` Id property) resolves to this animation.
// Coop-delivered KeyedObjects live in the EditorFile arena and
// don't fit the `unique_ptr` ownership contract used by the `.riv`
// importer path — parallel storage keeps both loading modes
// coexistent within a single editor binary: a runtime `.riv`
// loaded side-by-side with a coop file uses `m_KeyedObjects`,
// the coop-loaded one uses `m_EditorKeyedObjects`. Iteration
// (apply / onAddedDirty / onAddedClean / reportKeyedCallbacks)
// walks whichever is populated — for any given `LinearAnimation`
// instance exactly one side is non-empty in practice.
std::vector<KeyedObject*> m_EditorKeyedObjects;
#endif
friend class Artboard;
public:
LinearAnimation();
~LinearAnimation() override;
StatusCode onAddedDirty(CoreContext* context) override;
StatusCode onAddedClean(CoreContext* context) override;
void addKeyedObject(std::unique_ptr<KeyedObject>);
/// `context` is the running LinearAnimationInstance, threaded down so
/// scripted interpolators can vend per-(LAI, keyframe) stateful clones.
/// Default-null keeps direct callers (state-machine hold animations,
/// tests) source-compatible — they degrade to identity for any scripted
/// interpolators in the snapshot.
void apply(Artboard* artboard,
float time,
float mix = 1.0f,
const LinearAnimationInstance* context = nullptr) const;
#ifdef WITH_RIVE_EDITOR
// Editor-only: push a coop-hydrated KeyedObject onto
// `m_EditorKeyedObjects`. See field comment for lifetime.
void addKeyedObjectForEditor(KeyedObject* object);
// Editor-only: drop editor-only entries so `finalizeBatch` can
// rebuild idempotently on each coop batch.
void clearEditorKeyedObjects();
// Editor-only: how many coop-hydrated KeyedObjects this animation
// carries. Used by diagnostics to distinguish "empty" animations
// from real playback candidates.
size_t editorKeyedObjectCount() const
{
return m_EditorKeyedObjects.size();
}
/// Editor-only: read-only view of the coop-hydrated KeyedObjects.
/// Used by editor_native's timeline FFI to build the row tree.
/// Pointer lifetime matches the EditorFile arena — entries are
/// repopulated on every `finalizeBatch`, so iterate within the
/// drain that called it.
const std::vector<KeyedObject*>& editorKeyedObjects() const
{
return m_EditorKeyedObjects;
}
#endif
Loop loop() const { return (Loop)loopValue(); }
StatusCode import(ImportStack& importStack) override;
float durationSeconds() const;
/// Returns the start time/ end time of the animation in seconds
float startSeconds() const;
float endSeconds() const;
/// Returns the start time/ end time of the animation in seconds,
/// considering speed
float startTime() const;
float startTime(float multiplier) const;
float endTime() const;
/// Convert a global clock to local seconds (takes into consideration
/// work area start/end, speed, looping).
float globalToLocalSeconds(float seconds) const;
// Returns a list of only the KeyedObjects that were validated during
// onAddedDirty. This is not guaranteed to be the same as the list in the
// exported riv.
const KeyedObject* getObject(size_t index) const
{
if (index < m_KeyedObjects.size())
{
return m_KeyedObjects[index].get();
}
else
{
return nullptr;
}
}
size_t numKeyedObjects() const { return m_KeyedObjects.size(); }
#ifdef TESTING
// Used in testing to check how many animations gets deleted.
static int deleteCount;
#endif
void reportKeyedCallbacks(KeyedCallbackReporter* reporter,
float secondsFrom,
float secondsTo,
float speedDirection,
bool fromPong) const;
};
} // namespace rive
#endif