blob: e71a571d27ebd5d0f0c64b8acc1f04ec91356e85 [file] [log] [blame]
#include "rive/animation/linear_animation_instance.hpp"
#include "rive/animation/linear_animation.hpp"
#include "rive/animation/loop.hpp"
#include <cmath>
using namespace rive;
LinearAnimationInstance::LinearAnimationInstance(
const LinearAnimation* animation) :
m_Animation(animation),
m_Time(animation->enableWorkArea()
? (float)animation->workStart() / animation->fps()
: 0),
m_TotalTime(0.0f),
m_LastTotalTime(0.0f),
m_SpilledTime(0.0f),
m_Direction(1)
{
}
bool LinearAnimationInstance::advance(float elapsedSeconds)
{
const LinearAnimation& animation = *m_Animation;
m_Time += elapsedSeconds * animation.speed() * m_Direction;
m_LastTotalTime = m_TotalTime;
m_TotalTime += elapsedSeconds;
int fps = animation.fps();
float frames = m_Time * fps;
int start = animation.enableWorkArea() ? animation.workStart() : 0;
int end =
animation.enableWorkArea() ? animation.workEnd() : animation.duration();
int range = end - start;
bool keepGoing = true;
bool didLoop = false;
m_SpilledTime = 0.0f;
switch (loop())
{
case Loop::oneShot:
if (m_Direction == 1 && frames > end)
{
keepGoing = false;
m_SpilledTime = (frames - end) / fps;
frames = end;
m_Time = frames / fps;
didLoop = true;
}
else if (m_Direction == -1 && frames < start)
{
keepGoing = false;
m_SpilledTime = (start - frames) / fps;
frames = start;
m_Time = frames / fps;
didLoop = true;
}
break;
case Loop::loop:
if (m_Direction == 1 && frames >= end)
{
m_SpilledTime = (frames - end) / fps;
frames = m_Time * fps;
frames = start + std::fmod(frames - start, range);
m_Time = frames / fps;
didLoop = true;
}
else if (m_Direction == -1 && frames <= start)
{
m_SpilledTime = (start - frames) / fps;
frames = m_Time * fps;
frames = end - std::abs(std::fmod(start - frames, range));
m_Time = frames / fps;
didLoop = true;
}
break;
case Loop::pingPong:
while (true)
{
if (m_Direction == 1 && frames >= end)
{
m_SpilledTime = (frames - end) / fps;
m_Direction = -1;
frames = end + (end - frames);
m_Time = frames / fps;
didLoop = true;
}
else if (m_Direction == -1 && frames < start)
{
m_SpilledTime = (start - frames) / fps;
m_Direction = 1;
frames = start + (start - frames);
m_Time = frames / fps;
didLoop = true;
}
else
{
// we're within the range, we can stop fixing. We do this in
// a loop to fix conditions when time has advanced so far
// that we've ping-ponged back and forth a few times in a
// single frame. We want to accomodate for this in cases
// where animations are not advanced on regular intervals.
break;
}
}
break;
}
m_DidLoop = didLoop;
return keepGoing;
}
void LinearAnimationInstance::time(float value)
{
if (m_Time == value)
{
return;
}
m_Time = value;
// Make sure to keep last and total in relative lockstep so state machines
// can track change even when setting time.
auto diff = m_TotalTime - m_LastTotalTime;
int start = (m_Animation->enableWorkArea() ? m_Animation->workStart() : 0) *
m_Animation->fps();
m_TotalTime = value - start;
m_LastTotalTime = m_TotalTime - diff;
// leaving this RIGHT now. but is this required? it kinda messes up
// playing things backwards and seeking. what purpose does it solve?
m_Direction = 1;
}
// Returns either the animation's default or overridden loop values
int LinearAnimationInstance::loopValue()
{
if (m_LoopValue != -1)
{
return m_LoopValue;
}
return m_Animation->loopValue();
}
// Override the animation's loop value
void LinearAnimationInstance::loopValue(int value)
{
if (m_LoopValue == value)
{
return;
}
if (m_LoopValue == -1 && m_Animation->loopValue() == value)
{
return;
}
m_LoopValue = value;
}
float LinearAnimationInstance::durationSeconds() const
{
return m_Animation->durationSeconds();
}