Browse Source

Animation: added Player::elapsed().

It's not working exactly as it should yet, but the proper update
wouldn't apply so cleanly, so doing that in the next commit.
pull/255/merge
Vladimír Vondruš 8 years ago
parent
commit
8a71cb6415
  1. 22
      src/Magnum/Animation/Player.h
  2. 72
      src/Magnum/Animation/Player.hpp
  3. 101
      src/Magnum/Animation/Test/PlayerTest.cpp

22
src/Magnum/Animation/Player.h

@ -287,6 +287,7 @@ template<class T, class K
* If the duration was not set explicitly using @ref setDuration(),
* returns value calculated implicitly from all added tracks. If no
* tracks are added, returns default-constructed value.
* @see @ref elapsed()
*/
Math::Range1D<K> duration() const { return _duration; }
@ -593,6 +594,24 @@ template<class T, class K
*/
State state() const { return _state; }
/**
* @brief Elapsed animation iteration and keyframe
*
* Returns repeat iteration index and elapsed animation keyframe in
* given iteration corresponding to @p time. If @ref state() is
* @ref State::Stopped and the player was stopped explicitly, the
* function returns a default-constructed value (usually
* @cpp {0, 0.0f} @ce). If @ref state() is @ref State::Stopped due to
* the animation running out, the function returns the iteration count
* and duration end keyframe. If @ref state() is @ref State::Paused,
* the function returns a time at which the animation was paused.
*
* Unlike @ref advance(), this function doesn't modify the animation
* state in any way, it's merely a query.
* @see @ref duration()
*/
std::pair<UnsignedInt, K> elapsed(T time) const;
/**
* @brief Play
*
@ -677,6 +696,7 @@ template<class T, class K
* to the begin time of @ref duration() to correctly "park" the
* animation back to its initial state. After that, no more updates are
* done until the animation is started again.
* @see @ref elapsed()
*/
Player<T, K>& advance(T time);
@ -685,6 +705,8 @@ template<class T, class K
Player<T, K>& addInternal(const TrackViewStorage<K>& track, void (*advancer)(const TrackViewStorage<K>&, K, std::size_t&, void*, void(*)(), void*), void* destination, void(*userCallback)(), void* userCallbackData);
Containers::Optional<std::pair<UnsignedInt, K>> elapsedInternal(T time, T& updatedStartTime, T& updatedPauseTime, State& updatedState) const;
std::vector<Track> _tracks;
Math::Range1D<K> _duration;
UnsignedInt _playCount{1};

72
src/Magnum/Animation/Player.hpp

@ -31,6 +31,8 @@
#include "Player.h"
#include <Corrade/Containers/Optional.h>
namespace Magnum { namespace Animation {
namespace Implementation {
@ -140,7 +142,9 @@ template<class T, class K> Player<T, K>& Player<T, K>::setState(State state, T t
CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
template<class T, class K> Player<T, K>& Player<T, K>::advance(T time) {
namespace Implementation {
template<class T, class K> Containers::Optional<std::pair<UnsignedInt, K>> playerElapsed(const K duration, const UnsignedInt playCount, const typename Player<T, K>::Scaler scaler, const T time, T& startTime, T& pauseTime, State& state) {
/* Time to use for advancing the animation */
T timeToUse = time;
@ -150,49 +154,81 @@ template<class T, class K> Player<T, K>& Player<T, K>::advance(T time) {
std::chrono::duration doesn't have operator bool, so I need to compare
to default-constructed value. Ugh. */
if(_state == State::Paused && (_pauseTime != T{})) {
timeToUse = _pauseTime;
_startTime = _pauseTime - _startTime;
_pauseTime = {};
if(state == State::Paused && (pauseTime != T{})) {
timeToUse = pauseTime;
startTime = pauseTime - startTime;
pauseTime = {};
/* The animation was stopped by the user right before this iteration,
"park" the animation to the initial time */
} else if(_state == State::Stopped && (_startTime != T{})) {
_startTime = {};
} else if(state == State::Stopped && (startTime != T{})) {
startTime = {};
timeToUse = {};
/* Otherwise, if the player is not playing or scheduled to start playing in
the future, do nothing */
} else if(_state != State::Playing || time < _startTime) return *this;
} else if(state != State::Playing || time < startTime)
return Containers::NullOpt;
/* If the player duration is empty, we can't call the scaler. If play count
is infinite, infinitely advance to a key at duration start. If not, stop
the animation. */
UnsignedInt playIteration;
K key;
const K duration = _duration.size()[0];
if(duration == K{}) {
key = K{};
if(_playCount != 0) {
_state = State::Stopped;
_startTime = {};
playIteration = 0;
if(playCount != 0) {
state = State::Stopped;
startTime = {};
}
/* Otherwise calculate current play iteration and key value in that
iteration. If we exceeded play count, stop the animation and give out
value at duration end. */
} else {
UnsignedInt playCount;
std::tie(playCount, key) = _scaler(timeToUse - _startTime, duration);
if(_playCount && playCount >= _playCount) {
_state = State::Stopped;
_startTime = {};
std::tie(playIteration, key) = scaler(timeToUse - startTime, duration);
if(playCount && playIteration >= playCount) {
state = State::Stopped;
startTime = {}; /** @todo no? so we can distinguish between stopped by itself and not */
playIteration = playCount - 1;
key = duration;
}
}
return {Containers::InPlaceInit, playIteration, key};
}
}
template<class T, class K> std::pair<UnsignedInt, K> Player<T, K>::elapsed(const T time) const {
/* Get the elapsed time. This is an immutable query, so make copies of the
(otherwise to be modified) internal state. */
T startTime = _startTime;
T pauseTime = _pauseTime;
State state = _state;
const Containers::Optional<std::pair<UnsignedInt, K>> elapsed = Implementation::playerElapsed(_duration.size()[0], _playCount, _scaler, time, startTime, pauseTime, state);
if(elapsed) return *elapsed;
/* If not advancing, the animation can be paused: calculate the keyframe at
which it was paused */
if(state == State::Paused) return _scaler(_startTime, _duration.size()[0]);
/** @todo stopped by itself vs. explicitly */
/* Otherwise the animation is just stopped: return a zero value */
return {0, K{}};
}
template<class T, class K> Player<T, K>& Player<T, K>::advance(const T time) {
/* Get the elapsed time. If we shouldn't advance anything (player already
stopped / not yet playing, quit */
Containers::Optional<std::pair<UnsignedInt, K>> elapsed = Implementation::playerElapsed(_duration.size()[0], _playCount, _scaler, time, _startTime, _pauseTime, _state);
if(!elapsed) return *this;
/* Advance all tracks. Properly handle durations that don't start at 0. */
for(Track& t: _tracks)
t.advancer(t.track, _duration.min()[0] + key, t.hint, t.destination, t.userCallback, t.userCallbackData);
t.advancer(t.track, _duration.min()[0] + elapsed->second, t.hint, t.destination, t.userCallback, t.userCallbackData);
return *this;
}

101
src/Magnum/Animation/Test/PlayerTest.cpp

@ -295,11 +295,17 @@ void PlayerTest::advanceNotRunning() {
player.add(Track, value);
CORRADE_COMPARE(player.state(), State::Stopped);
CORRADE_COMPARE(player.elapsed(0.0f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
/* Asking for elapsed doesn't change anything */
CORRADE_COMPARE(player.elapsed(1.75f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
player.advance(1.75f);
CORRADE_COMPARE(player.state(), State::Stopped);
CORRADE_COMPARE(player.elapsed(1.75f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
}
@ -311,32 +317,58 @@ void PlayerTest::advancePlaying() {
CORRADE_COMPARE(player.duration().size(), 3.0f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(0.0f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
/* Still before starting time, nothing is done */
player.advance(1.75f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(1.75f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
/* Asking for elapsed will say it's playing already, but doesn't change
anything */
CORRADE_COMPARE(player.elapsed(3.75f), std::make_pair(0, 1.75f));
CORRADE_COMPARE(value, -1.0f);
/* 1.75 secs in */
player.advance(3.75f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(3.75f), std::make_pair(0, 1.75f));
CORRADE_COMPARE(value, 4.0f);
/* 2.67 secs in */
player.advance(4.6666667f);
player.advance(4.666666667f);
CORRADE_COMPARE(player.state(), State::Playing);
/** @todo make std::pair/std::tuple comparisons respect fuzzyness inside */
CORRADE_COMPARE(player.elapsed(4.666666667f).first, 0);
CORRADE_COMPARE(player.elapsed(4.666666667f).second, 2.666666667f);
CORRADE_COMPARE(value, 3.0f);
/* When the player gets stopped, the value at the stop time is written */
/* Asking for elapsed will say the stop time, but again doesn't change the
state */
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(5.5f), std::make_pair(0, 3.0f));
CORRADE_COMPARE(value, 3.0f);
/* When the player gets stopped, the value at the stop time is written.
Elapsed time still shows that it stopped by itself. */
player.advance(5.5f);
CORRADE_COMPARE(player.state(), State::Stopped);
{
CORRADE_EXPECT_FAIL("Not yet implemented.");
CORRADE_COMPARE(player.elapsed(5.5f), std::make_pair(0, 3.0f));
}
CORRADE_COMPARE(value, 2.0f);
/* But further advancing will not write anything */
value = -1.0f;
player.advance(100.0f);
CORRADE_COMPARE(player.state(), State::Stopped);
{
CORRADE_EXPECT_FAIL("Not yet implemented.");
CORRADE_COMPARE(player.elapsed(100.0f), std::make_pair(0, 3.0f));
}
CORRADE_COMPARE(value, -1.0f);
}
@ -347,27 +379,32 @@ void PlayerTest::advanceRestart() {
.play(2.0f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(0.0f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
/* Still before starting time, nothing is done */
player.advance(1.75f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(1.75f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
/* 1.75 secs in */
player.advance(3.75f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(3.75f), std::make_pair(0, 1.75f));
CORRADE_COMPARE(value, 4.0f);
/* Call play again, will restart from the beginning... */
value = -1.0f;
player.play(4.0f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(4.0f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
/* ... but only after calling advance() again. Now at 1 sec in. */
player.advance(5.0f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(5.0f), std::make_pair(0, 1.0f));
CORRADE_COMPARE(value, 2.5f);
}
@ -379,23 +416,29 @@ void PlayerTest::advanceStop() {
player.advance(3.75f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(3.75f), std::make_pair(0, 1.75f));
CORRADE_COMPARE(value, 4.0f);
/* Stop, should not update anything */
/* Stop, should not update anything. Elapsed will report a time from the
beginning again. */
value = -1.0;
player.stop();
CORRADE_COMPARE(player.state(), State::Stopped);
CORRADE_COMPARE(player.elapsed(5.0f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
/* Advancing will update with a value from beginning of the duration */
/* Advancing will update with a value from beginning of the duration.
Elapsed shows the same. */
player.advance(5.0f);
CORRADE_COMPARE(player.state(), State::Stopped);
CORRADE_COMPARE(player.elapsed(5.0f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, 1.5f);
/* But further advancing will not write anything */
value = -1.0f;
player.advance(100.0f);
CORRADE_COMPARE(player.state(), State::Stopped);
CORRADE_COMPARE(player.elapsed(100.0f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
}
@ -406,43 +449,52 @@ void PlayerTest::advancePauseResume() {
.play(2.0f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(3.75f), std::make_pair(0, 1.75f));
CORRADE_COMPARE(value, -1.0f);
player.advance(3.75f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(3.75f), std::make_pair(0, 1.75f));
CORRADE_COMPARE(value, 4.0f);
/* Pausing should not update anything */
value = -1.0f;
player.pause(4.0f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(4.0f), std::make_pair(0, 2.0f));
CORRADE_COMPARE(value, -1.0f);
/* Pausing again should be a no-op */
player.pause(4.1f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(4.1f), std::make_pair(0, 2.0f));
CORRADE_COMPARE(value, -1.0f);
/* But advance() after should. No matter what time is passed to it, it
should update with time of pause. */
player.advance(4.5f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(4.5f), std::make_pair(0, 2.0f));
CORRADE_COMPARE(value, 5.0f); /* value at 2.0f, not 2.5f */
/* Advancing further should do nothing */
value = -1.0f;
player.advance(50.0f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(50.0f), std::make_pair(0, 2.0f));
CORRADE_COMPARE(value, -1.0f);
/* Resuming the animation, again should not update anything */
player.play(100.0f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(100.0f), std::make_pair(0, 2.0f));
CORRADE_COMPARE(value, -1.0f);
/* Advancing the animation should update again. It was paused after two
seconds, so continuing at 2.5 seconds now. */
player.advance(100.5f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(100.5f), std::make_pair(0, 2.5f));
CORRADE_COMPARE(value, 3.5f);
}
@ -454,35 +506,41 @@ void PlayerTest::advancePauseStop() {
player.advance(3.75f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(3.75f), std::make_pair(0, 1.75f));
CORRADE_COMPARE(value, 4.0f);
/* Pause, get value from the pause time */
player.pause(4.0f);
player.advance(4.5f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(3.75f), std::make_pair(0, 2.0f));
CORRADE_COMPARE(value, 5.0f);
/* Stop, should not update anything */
value = -1.0;
player.stop();
CORRADE_COMPARE(player.state(), State::Stopped);
CORRADE_COMPARE(player.elapsed(5.0f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
/* Advancing will update with a value from beginning of the duration */
player.advance(5.0f);
CORRADE_COMPARE(player.state(), State::Stopped);
CORRADE_COMPARE(player.elapsed(5.0f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, 1.5f);
/* But further advancing will not write anything */
value = -1.0f;
player.advance(100.0f);
CORRADE_COMPARE(player.state(), State::Stopped);
CORRADE_COMPARE(player.elapsed(100.0f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
/* Pause while stopped is a no-op */
player.pause(101.0f);
player.advance(101.0f);
CORRADE_COMPARE(player.state(), State::Stopped);
CORRADE_COMPARE(player.elapsed(101.0f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
}
@ -494,37 +552,50 @@ void PlayerTest::advancePlayCount() {
.play(2.0f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(1.75f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
/* Still before starting time, nothing is done */
player.advance(1.75f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(1.75f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
/* 1.75 secs in */
player.advance(3.75f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(3.75f), std::make_pair(0, 1.75f));
CORRADE_COMPARE(value, 4.0f);
/* 2 secs in, second round */
player.advance(7.0f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(7.0f), std::make_pair(1, 2.0f));
CORRADE_COMPARE(value, 5.0f);
/* 1.75 secs in, third round */
player.advance(9.75f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(9.75f), std::make_pair(2, 1.75f));
CORRADE_COMPARE(value, 4.0f);
/* When the player gets stopped, the value at the stop time is written */
player.advance(11.5f);
CORRADE_COMPARE(player.state(), State::Stopped);
{
CORRADE_EXPECT_FAIL("Not yet implemented.");
CORRADE_COMPARE(player.elapsed(11.5f), std::make_pair(2, 3.0f));
}
CORRADE_COMPARE(value, 2.0f);
/* But further advancing will not write anything */
value = -1.0f;
player.advance(100.0f);
CORRADE_COMPARE(player.state(), State::Stopped);
{
CORRADE_EXPECT_FAIL("Not yet implemented.");
CORRADE_COMPARE(player.elapsed(100.0f), std::make_pair(2, 3.0f));
}
CORRADE_COMPARE(value, -1.0f);
}
@ -536,26 +607,31 @@ void PlayerTest::advancePlayCountInfinite() {
.play(2.0f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(1.75f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
/* Still before starting time, nothing is done */
player.advance(1.75f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(1.75f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
/* 1.75 secs in */
player.advance(3.75f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(3.75f), std::make_pair(0, 1.75f));
CORRADE_COMPARE(value, 4.0f);
/* 2 secs in, second round */
player.advance(7.0f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(7.0f), std::make_pair(1, 2.0f));
CORRADE_COMPARE(value, 5.0f);
/* 1.75 secs in, 10th round */
/* 1.75 secs in, 11th round */
player.advance(33.75f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(33.75f), std::make_pair(10, 1.75f));
CORRADE_COMPARE(value, 4.0f);
}
@ -567,16 +643,22 @@ void PlayerTest::advanceChrono() {
CORRADE_COMPARE(player.duration().size(), 3.0f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(std::chrono::milliseconds{1750}),
std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
/* Still before starting time, nothing is done */
player.advance(std::chrono::milliseconds{1750});
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(std::chrono::milliseconds{1750}),
std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
/* 1.75 secs in */
player.advance(std::chrono::milliseconds{3750});
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(std::chrono::milliseconds{3750}),
std::make_pair(0, 1.75f));
CORRADE_COMPARE(value, 4.0f);
}
@ -591,16 +673,19 @@ void PlayerTest::advanceZeroDuration() {
CORRADE_COMPARE(player.duration().size(), 0.0f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(1.75f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
/* Still before starting time, nothing is done */
player.advance(1.75f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(1.75f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
/* After that, the value at 1.75 secs is returned independent of time */
player.advance(100.0f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(100.0f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, 4.0f);
}
@ -615,17 +700,23 @@ void PlayerTest::advanceZeroDurationChrono() {
CORRADE_COMPARE(player.duration().size(), 0.0f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(std::chrono::milliseconds{1750}),
std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
/* Still before starting time, nothing is done */
player.advance(std::chrono::milliseconds{1750});
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(std::chrono::milliseconds{1750}),
std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
/* After that, the value at 1.75 seconds is returned independent of the
time */
player.advance(std::chrono::seconds{100});
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(std::chrono::seconds{100}),
std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, 4.0f);
}

Loading…
Cancel
Save