From de995059c6eec84f3d91ccd3167e9c09ec7d1078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 10 Sep 2018 20:23:59 +0200 Subject: [PATCH] Animation: make Player::elapsed() behave correctly after a "run out". --- src/Magnum/Animation/Player.h | 2 +- src/Magnum/Animation/Player.hpp | 51 +++++++++++++++--------- src/Magnum/Animation/Test/PlayerTest.cpp | 20 ++-------- 3 files changed, 38 insertions(+), 35 deletions(-) diff --git a/src/Magnum/Animation/Player.h b/src/Magnum/Animation/Player.h index 6450447b5..6b9b00067 100644 --- a/src/Magnum/Animation/Player.h +++ b/src/Magnum/Animation/Player.h @@ -711,7 +711,7 @@ template _duration; UnsignedInt _playCount{1}; State _state{State::Stopped}; - T _startTime{}, _pauseTime{}; + T _startTime{}, _stopPauseTime{}; Scaler _scaler; }; diff --git a/src/Magnum/Animation/Player.hpp b/src/Magnum/Animation/Player.hpp index 1496bee2c..f14c9a595 100644 --- a/src/Magnum/Animation/Player.hpp +++ b/src/Magnum/Animation/Player.hpp @@ -123,12 +123,15 @@ template Player& Player::pause(T pauseTime) { if(_state != State::Playing) return *this; _state = State::Paused; - _pauseTime = pauseTime; + _stopPauseTime = pauseTime; return *this; } template Player& Player::stop() { _state = State::Stopped; + /* Anything, just not a default-constructed value */ + /** @todo might be problematic for some types */ + _stopPauseTime = T{1}; return *this; } @@ -144,7 +147,7 @@ template Player& Player::setState(State state, T t namespace Implementation { -template Containers::Optional> playerElapsed(const K duration, const UnsignedInt playCount, const typename Player::Scaler scaler, const T time, T& startTime, T& pauseTime, State& state) { +template Containers::Optional> playerElapsed(const K duration, const UnsignedInt playCount, const typename Player::Scaler scaler, const T time, T& startTime, T& stopPauseTime, State& state) { /* Time to use for advancing the animation */ T timeToUse = time; @@ -154,16 +157,17 @@ template Containers::Optional> playe 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 && (stopPauseTime != T{})) { + timeToUse = stopPauseTime; + startTime = stopPauseTime - startTime; + stopPauseTime = {}; /* 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 && (stopPauseTime != T{})) { timeToUse = {}; + startTime = {}; + stopPauseTime = {}; /* Otherwise, if the player is not playing or scheduled to start playing in the future, do nothing */ @@ -190,7 +194,8 @@ template Containers::Optional> playe 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 */ + /* Don't reset the startTime to disambiguate between explicitly + stopped and "time run out" animation */ playIteration = playCount - 1; key = duration; } @@ -205,25 +210,35 @@ template std::pair Player::elapsed(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; + T pauseTime = _stopPauseTime; State state = _state; - const Containers::Optional> elapsed = Implementation::playerElapsed(_duration.size()[0], _playCount, _scaler, time, startTime, pauseTime, state); + const K duration = _duration.size()[0]; + const Containers::Optional> elapsed = Implementation::playerElapsed(duration, _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 */ + /* If not advancing, the animation can be paused -- calculate the iteration + index and keyframe at which it was paused if the duration is nonzero. */ + if(_state == State::Paused && duration) + return _scaler(_startTime, duration); + + /* It can be also stopped by running out, in that case return the last + iteration index and the duration. Again have to use comparison to + default-constructed value because std::chrono::nanoseconds doesn't have + operator bool. */ + if(_state == State::Stopped && _startTime != T{}) { + CORRADE_INTERNAL_ASSERT(_playCount); + return {_playCount - 1, duration}; + } - /* Otherwise the animation is just stopped: return a zero value */ + /* Otherwise (zero duration, explicitly stopped, not yet started) return + zero */ return {0, K{}}; } template Player& Player::advance(const T time) { /* Get the elapsed time. If we shouldn't advance anything (player already stopped / not yet playing, quit */ - Containers::Optional> elapsed = Implementation::playerElapsed(_duration.size()[0], _playCount, _scaler, time, _startTime, _pauseTime, _state); + Containers::Optional> elapsed = Implementation::playerElapsed(_duration.size()[0], _playCount, _scaler, time, _startTime, _stopPauseTime, _state); if(!elapsed) return *this; /* Advance all tracks. Properly handle durations that don't start at 0. */ diff --git a/src/Magnum/Animation/Test/PlayerTest.cpp b/src/Magnum/Animation/Test/PlayerTest.cpp index 7793eda03..084a9d37e 100644 --- a/src/Magnum/Animation/Test/PlayerTest.cpp +++ b/src/Magnum/Animation/Test/PlayerTest.cpp @@ -355,20 +355,14 @@ void PlayerTest::advancePlaying() { 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(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(player.elapsed(100.0f), std::make_pair(0, 3.0f)); CORRADE_COMPARE(value, -1.0f); } @@ -582,20 +576,14 @@ void PlayerTest::advancePlayCount() { /* 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(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(player.elapsed(100.0f), std::make_pair(2, 3.0f)); CORRADE_COMPARE(value, -1.0f); }