blob: 750e976d8e03433b5ec5b5eef69b6d8953323d3d [file] [log] [blame]
#include "rive/animation/keyed_property.hpp"
#include "rive/animation/keyed_object.hpp"
#include "rive/animation/keyframe.hpp"
#include "rive/animation/interpolating_keyframe.hpp"
#include "rive/animation/keyed_callback_reporter.hpp"
#include "rive/importers/import_stack.hpp"
#include "rive/importers/keyed_object_importer.hpp"
using namespace rive;
KeyedProperty::KeyedProperty() {}
KeyedProperty::~KeyedProperty() {}
void KeyedProperty::addKeyFrame(std::unique_ptr<KeyFrame> keyframe)
{
m_keyFrames.push_back(std::move(keyframe));
}
int KeyedProperty::closestFrameIndex(float seconds, int exactOffset) const
{
int idx = 0;
int mid = 0;
float closestSeconds = 0;
int start = 0;
auto numKeyFrames = static_cast<int>(m_keyFrames.size());
int end = numKeyFrames - 1;
while (start <= end)
{
mid = (start + end) >> 1;
closestSeconds = m_keyFrames[mid]->seconds();
if (closestSeconds < seconds)
{
start = mid + 1;
}
else if (closestSeconds > seconds)
{
end = mid - 1;
}
else
{
return mid + exactOffset;
}
idx = start;
}
return idx;
}
void KeyedProperty::reportKeyedCallbacks(KeyedCallbackReporter* reporter,
uint32_t objectId,
float secondsFrom,
float secondsTo,
int secondsFromExactOffset) const
{
int idx = closestFrameIndex(secondsFrom, secondsFromExactOffset);
int idxTo = closestFrameIndex(secondsTo, 1);
if (idxTo < idx)
{
auto swap = idx;
idx = idxTo;
idxTo = swap;
}
while (idxTo > idx)
{
const std::unique_ptr<KeyFrame>& frame = m_keyFrames[idx];
reporter->reportKeyedCallback(objectId, propertyKey(), secondsTo - frame->seconds());
idx++;
}
}
void KeyedProperty::apply(Core* object, float seconds, float mix)
{
assert(!m_keyFrames.empty());
int idx = closestFrameIndex(seconds);
int pk = propertyKey();
if (idx == 0)
{
static_cast<InterpolatingKeyFrame*>(m_keyFrames[0].get())->apply(object, pk, mix);
}
else
{
if (idx < static_cast<int>(m_keyFrames.size()))
{
InterpolatingKeyFrame* fromFrame =
static_cast<InterpolatingKeyFrame*>(m_keyFrames[idx - 1].get());
InterpolatingKeyFrame* toFrame =
static_cast<InterpolatingKeyFrame*>(m_keyFrames[idx].get());
if (seconds == toFrame->seconds())
{
toFrame->apply(object, pk, mix);
}
else
{
if (fromFrame->interpolationType() == 0)
{
fromFrame->apply(object, pk, mix);
}
else
{
fromFrame->applyInterpolation(object, pk, seconds, toFrame, mix);
}
}
}
else
{
static_cast<InterpolatingKeyFrame*>(m_keyFrames[idx - 1].get())->apply(object, pk, mix);
}
}
}
StatusCode KeyedProperty::onAddedDirty(CoreContext* context)
{
StatusCode code;
for (auto& keyframe : m_keyFrames)
{
if ((code = keyframe->onAddedDirty(context)) != StatusCode::Ok)
{
return code;
}
}
return StatusCode::Ok;
}
StatusCode KeyedProperty::onAddedClean(CoreContext* context)
{
StatusCode code;
for (auto& keyframe : m_keyFrames)
{
if ((code = keyframe->onAddedClean(context)) != StatusCode::Ok)
{
return code;
}
}
return StatusCode::Ok;
}
StatusCode KeyedProperty::import(ImportStack& importStack)
{
auto importer = importStack.latest<KeyedObjectImporter>(KeyedObjectBase::typeKey);
if (importer == nullptr)
{
return StatusCode::MissingObject;
}
importer->addKeyedProperty(std::unique_ptr<KeyedProperty>(this));
return Super::import(importStack);
}