Browse Source

Animation: new Track and TrackView containers.

pull/191/head
Vladimír Vondruš 8 years ago
parent
commit
8cc560eadc
  1. 1
      doc/snippets/CMakeLists.txt
  2. 102
      doc/snippets/MagnumAnimation.cpp
  3. 3
      src/Magnum/Animation/Animation.h
  4. 3
      src/Magnum/Animation/CMakeLists.txt
  5. 11
      src/Magnum/Animation/Interpolation.h
  6. 4
      src/Magnum/Animation/Test/CMakeLists.txt
  7. 223
      src/Magnum/Animation/Test/TrackTest.cpp
  8. 193
      src/Magnum/Animation/Test/TrackViewTest.cpp
  9. 390
      src/Magnum/Animation/Track.h

1
doc/snippets/CMakeLists.txt

@ -40,6 +40,7 @@ endif()
add_library(snippets-Magnum STATIC
Magnum.cpp
MagnumAnimation.cpp
MagnumMath.cpp)
target_link_libraries(snippets-Magnum PRIVATE Magnum)
set_target_properties(snippets-Magnum PROPERTIES FOLDER "Magnum/doc/snippets")

102
doc/snippets/MagnumAnimation.cpp

@ -0,0 +1,102 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include "Magnum/Math/Quaternion.h"
#include "Magnum/Animation/Track.h"
using namespace Magnum;
using namespace Magnum::Math::Literals;
int main() {
{
/* [Track-usage] */
const Animation::Track<Float, Vector2> jump{{
{0.0f, Vector2::yAxis(0.0f)},
{1.0f, Vector2::yAxis(0.5f)},
{2.0f, Vector2::yAxis(0.75f)},
{3.0f, Vector2::yAxis(0.875f)},
{4.0f, Vector2::yAxis(0.75f)},
{5.0f, Vector2::yAxis(0.5f)},
{6.0f, Vector2::yAxis(0.0f)}
}, Math::lerp, Animation::Extrapolation::Constant};
Vector2 position = jump.at(2.2f); // y = 0.775
/* [Track-usage] */
{
/* [Track-performance-hint] */
std::size_t hint = 0;
Vector2 position = jump.at(2.2f, hint); // y = 0.775, hint = 2
/* [Track-performance-hint] */
static_cast<void>(position);
}
{
/* [Track-performance-strict] */
std::size_t hint = 0;
Vector2 position = jump.atStrict(2.2f, hint); // y = 0.775, hint = 2
/* [Track-performance-strict] */
static_cast<void>(position);
}
static_cast<void>(position);
}
{
/* [Track-performance-cache] */
struct Keyframe {
Float time;
Vector2 position;
Deg rotation;
};
const Keyframe data[]{
{0.0f, Vector2::yAxis(0.0f), 0.0_degf},
{1.0f, Vector2::yAxis(0.5f), 60.0_degf},
{2.0f, Vector2::yAxis(0.75f), 80.0_degf},
{3.0f, Vector2::yAxis(0.875f), 90.0_degf},
{4.0f, Vector2::yAxis(0.75f), 100.0_degf},
{5.0f, Vector2::yAxis(0.5f), 120.0_degf},
{6.0f, Vector2::yAxis(0.0f), 180.0_degf}
};
Animation::TrackView<Float, Vector2> positions{
{&data[0].time, Containers::arraySize(data), sizeof(Keyframe)},
{&data[0].position, Containers::arraySize(data), sizeof(Keyframe)},
Math::lerp};
Animation::TrackView<Float, Deg> rotations{
{&data[0].time, Containers::arraySize(data), sizeof(Keyframe)},
{&data[0].rotation, Containers::arraySize(data), sizeof(Keyframe)},
Math::lerp};
Float time = 2.2f;
std::size_t hint = 0;
Vector2 position = positions.atStrict(time, hint); // y = 0.775f
Deg rotation = rotations.atStrict(time, hint); // φ = 82°
/* [Track-performance-cache] */
static_cast<void>(position);
static_cast<void>(rotation);
}
}

3
src/Magnum/Animation/Animation.h

@ -41,6 +41,9 @@ template<class V> using ResultOf = typename Implementation::TypeTraits<V>::Resul
enum class Extrapolation: UnsignedByte;
template<class, class> class Track;
template<class, class> class TrackView;
}}
#endif

3
src/Magnum/Animation/CMakeLists.txt

@ -25,7 +25,8 @@
set(MagnumAnimation_HEADERS
Animation.h
Interpolation.h)
Interpolation.h
Track.h)
# Force IDEs to display all header files in project view
add_custom_target(MagnumAnimation SOURCES ${MagnumAnimation_HEADERS})

11
src/Magnum/Animation/Interpolation.h

@ -63,7 +63,8 @@ template<class V> using ResultOf = typename Implementation::TypeTraits<V>::Resul
Describes what value is returned for frames outside of keyframe range for given
track (frame lower than first keyframe or frame larger or equal to last
keyframe).
@see @ref interpolate()
@see @ref interpolate(), @ref Track::before(), @ref Track::after(),
@ref TrackView::before(), @ref TrackView::after()
@experimental
*/
enum class Extrapolation: UnsignedByte {
@ -120,6 +121,10 @@ calculated interpolation factor, returning the interpolated value.
The @p hint parameter hints where to start the linear search and is updated
with keyframe index matching @p frame. If @p frame is earlier than @p hint, the
search is restarted from the beginning.
Used internally from @ref Track::at() / @ref TrackView::at(), see @ref Track
documentation for more information.
@see @ref interpolateStrict(), @ref Math::select(), @ref Math::lerp(),
@ref Math::slerp(), @ref Math::sclerp()
@experimental
@ -139,6 +144,10 @@ restarted from the beginning.
This is a stricter but more performant version of @ref interpolate() with
implicit @ref Extrapolation::Extrapolated behavior. Expects that there are
always at least two keyframes.
Used internally from @ref Track::atStrict() / @ref TrackView::atStrict(), see
@ref Track documentation for more information.
@see @ref Math::select(), @ref Math::lerp(), @ref Math::slerp(),
@ref Math::sclerp()
@experimental

4
src/Magnum/Animation/Test/CMakeLists.txt

@ -24,6 +24,8 @@
#
corrade_add_test(AnimationInterpolationTest InterpolationTest.cpp LIBRARIES Magnum)
corrade_add_test(AnimationTrackTest TrackTest.cpp LIBRARIES Magnum)
corrade_add_test(AnimationTrackViewTest TrackViewTest.cpp LIBRARIES Magnum)
set_property(TARGET
AnimationInterpolationTest
@ -31,4 +33,6 @@ set_property(TARGET
set_target_properties(
AnimationInterpolationTest
AnimationTrackTest
AnimationTrackViewTest
PROPERTIES FOLDER "Magnum/Animation/Test")

223
src/Magnum/Animation/Test/TrackTest.cpp

@ -0,0 +1,223 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <Corrade/TestSuite/Tester.h>
#include "Magnum/Animation/Track.h"
#include "Magnum/Math/Vector3.h"
namespace Magnum { namespace Animation { namespace Test {
struct TrackTest: TestSuite::Tester {
explicit TrackTest();
void constructArray();
void constructArrayDefaults();
void constructInitializerList();
void constructInitializerListDefaults();
void convertView();
void at();
void atStrict();
};
namespace {
/* Reduced version from InterpolateTest, keep in sync with TrackViewTest */
const struct {
const char* name;
Extrapolation extrapolationBefore;
Extrapolation extrapolationAfter;
Float time;
Float expectedValue, expectedValueStrict;
std::size_t expectedHint;
} AtData[] {
{"before default-constructed",
Extrapolation::DefaultConstructed, Extrapolation::Extrapolated,
-1.0f, 0.0f, 4.0f, 0},
{"before constant",
Extrapolation::Constant, Extrapolation::Extrapolated,
-1.0f, 3.0f, 4.0f, 0},
{"before extrapolated",
Extrapolation::Extrapolated, Extrapolation::DefaultConstructed,
-1.0f, 4.0f, 4.0f, 0},
{"during first",
Extrapolation::DefaultConstructed, Extrapolation::DefaultConstructed,
1.5f, 1.5f, 1.5f, 0},
{"during second",
Extrapolation::DefaultConstructed, Extrapolation::DefaultConstructed,
4.75f, 1.0f, 1.0f, 2},
{"after default-constructed",
Extrapolation::Extrapolated, Extrapolation::DefaultConstructed,
6.0f, 0.0f, -1.5f, 2},
{"after constant",
Extrapolation::Extrapolated, Extrapolation::Constant,
6.0f, 0.5f, -1.5f, 2},
{"after extrapolated",
Extrapolation::DefaultConstructed, Extrapolation::Extrapolated,
6.0f, -1.5f, -1.5f, 2}
};
}
TrackTest::TrackTest() {
addTests({&TrackTest::constructArray,
&TrackTest::constructArrayDefaults,
&TrackTest::constructInitializerList,
&TrackTest::constructInitializerListDefaults,
&TrackTest::convertView});
addInstancedTests({&TrackTest::at,
&TrackTest::atStrict}, Containers::arraySize(AtData));
}
using namespace Math::Literals;
void TrackTest::constructArray() {
const Track<Float, Vector3> a{
Containers::Array<std::pair<Float, Vector3>>{Containers::InPlaceInit,
{{0.0f, {3.0f, 1.0f, 0.1f}},
{5.0f, {0.3f, 0.6f, 1.0f}}}},
Math::select, Extrapolation::Extrapolated, Extrapolation::Constant};
CORRADE_COMPARE(a.interpolator(), Math::select);
CORRADE_COMPARE(a.before(), Extrapolation::Extrapolated);
CORRADE_COMPARE(a.after(), Extrapolation::Constant);
CORRADE_COMPARE(a.data().size(), 2);
CORRADE_COMPARE(a.keys().size(), 2);
CORRADE_COMPARE(a.values().size(), 2);
CORRADE_COMPARE(a[1], (std::pair<Float, Vector3>{5.0f, {0.3f, 0.6f, 1.0f}}));
CORRADE_COMPARE(a.data()[1], (std::pair<Float, Vector3>{5.0f, {0.3f, 0.6f, 1.0f}}));
CORRADE_COMPARE(a.keys()[1], 5.0f);
CORRADE_COMPARE(a.values()[0], (Vector3{3.0f, 1.0f, 0.1f}));
}
void TrackTest::constructArrayDefaults() {
const Track<Float, Vector3> a{
Containers::Array<std::pair<Float, Vector3>>{Containers::InPlaceInit,
{{0.0f, {3.0f, 1.0f, 0.1f}}}},
Math::lerp, Extrapolation::DefaultConstructed};
CORRADE_COMPARE(a.interpolator(), Math::lerp);
CORRADE_COMPARE(a.before(), Extrapolation::DefaultConstructed);
CORRADE_COMPARE(a.after(), Extrapolation::DefaultConstructed);
CORRADE_COMPARE(a.data().size(), 1);
CORRADE_COMPARE(a.keys().size(), 1);
CORRADE_COMPARE(a.values().size(), 1);
CORRADE_COMPARE(a[0], (std::pair<Float, Vector3>{0.0f, {3.0f, 1.0f, 0.1f}}));
CORRADE_COMPARE(a.data()[0], (std::pair<Float, Vector3>{0.0f, {3.0f, 1.0f, 0.1f}}));
CORRADE_COMPARE(a.keys()[0], 0.0f);
CORRADE_COMPARE(a.values()[0], (Vector3{3.0f, 1.0f, 0.1f}));
}
void TrackTest::constructInitializerList() {
const Track<Float, Vector3> a{
{{0.0f, {3.0f, 1.0f, 0.1f}},
{5.0f, {0.3f, 0.6f, 1.0f}}},
Math::select, Extrapolation::Extrapolated, Extrapolation::DefaultConstructed};
CORRADE_COMPARE(a.interpolator(), Math::select);
CORRADE_COMPARE(a.before(), Extrapolation::Extrapolated);
CORRADE_COMPARE(a.after(), Extrapolation::DefaultConstructed);
CORRADE_COMPARE(a.data().size(), 2);
CORRADE_COMPARE(a.keys().size(), 2);
CORRADE_COMPARE(a.values().size(), 2);
CORRADE_COMPARE(a[1], (std::pair<Float, Vector3>{5.0f, {0.3f, 0.6f, 1.0f}}));
CORRADE_COMPARE(a.data()[1], (std::pair<Float, Vector3>{5.0f, {0.3f, 0.6f, 1.0f}}));
CORRADE_COMPARE(a.keys()[1], 5.0f);
CORRADE_COMPARE(a.values()[0], (Vector3{3.0f, 1.0f, 0.1f}));
}
void TrackTest::constructInitializerListDefaults() {
const Track<Float, Vector3> a{{{0.0f, {3.0f, 1.0f, 0.1f}}},
Math::lerp, Extrapolation::Constant};
CORRADE_COMPARE(a.interpolator(), Math::lerp);
CORRADE_COMPARE(a.before(), Extrapolation::Constant);
CORRADE_COMPARE(a.after(), Extrapolation::Constant);
CORRADE_COMPARE(a.data().size(), 1);
CORRADE_COMPARE(a.keys().size(), 1);
CORRADE_COMPARE(a.values().size(), 1);
CORRADE_COMPARE(a[0], (std::pair<Float, Vector3>{0.0f, {3.0f, 1.0f, 0.1f}}));
CORRADE_COMPARE(a.data()[0], (std::pair<Float, Vector3>{0.0f, {3.0f, 1.0f, 0.1f}}));
CORRADE_COMPARE(a.keys()[0], 0.0f);
CORRADE_COMPARE(a.values()[0], (Vector3{3.0f, 1.0f, 0.1f}));
}
void TrackTest::convertView() {
const Track<Float, Vector3> a{
{{0.0f, {3.0f, 1.0f, 0.1f}},
{5.0f, {0.3f, 0.6f, 1.0f}}},
Math::select, Extrapolation::Extrapolated, Extrapolation::DefaultConstructed};
const TrackView<Float, Vector3> av = a;
CORRADE_COMPARE(av.interpolator(), Math::select);
CORRADE_COMPARE(av.before(), Extrapolation::Extrapolated);
CORRADE_COMPARE(av.after(), Extrapolation::DefaultConstructed);
CORRADE_COMPARE(av.keys().size(), 2);
CORRADE_COMPARE(av.values().size(), 2);
CORRADE_COMPARE(av[1], (std::pair<Float, Vector3>{5.0f, {0.3f, 0.6f, 1.0f}}));
CORRADE_COMPARE(av.keys()[1], 5.0f);
CORRADE_COMPARE(av.values()[0], (Vector3{3.0f, 1.0f, 0.1f}));
}
void TrackTest::at() {
const auto& data = AtData[testCaseInstanceId()];
setTestCaseDescription(data.name);
const Track<Float, Float> a{
{{0.0f, 3.0f},
{2.0f, 1.0f},
{4.0f, 2.5f},
{5.0f, 0.5f}}, Math::lerp,
data.extrapolationBefore, data.extrapolationAfter};
std::size_t hint{};
CORRADE_COMPARE(a.at(data.time, hint), data.expectedValue);
CORRADE_COMPARE(a.at(data.time), data.expectedValue);
CORRADE_COMPARE(hint, data.expectedHint);
}
void TrackTest::atStrict() {
const auto& data = AtData[testCaseInstanceId()];
setTestCaseDescription(data.name);
const Track<Float, Float> a{
{{0.0f, 3.0f},
{2.0f, 1.0f},
{4.0f, 2.5f},
{5.0f, 0.5f}}, Math::lerp,
data.extrapolationBefore, data.extrapolationAfter};
std::size_t hint{};
CORRADE_COMPARE(a.atStrict(data.time, hint), data.expectedValueStrict);
CORRADE_COMPARE(hint, data.expectedHint);
}
}}}
CORRADE_TEST_MAIN(Magnum::Animation::Test::TrackTest)

193
src/Magnum/Animation/Test/TrackViewTest.cpp

@ -0,0 +1,193 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <Corrade/TestSuite/Tester.h>
#include "Magnum/Animation/Track.h"
#include "Magnum/Math/Vector3.h"
namespace Magnum { namespace Animation { namespace Test {
struct TrackViewTest: TestSuite::Tester {
explicit TrackViewTest();
void construct();
void constructDefaults();
void constructSingleArray();
void constructSingleArrayDefaults();
void at();
void atStrict();
};
namespace {
/* Reduced version from InterpolateTest, keep in sync with TrackTest */
const struct {
const char* name;
Extrapolation extrapolationBefore;
Extrapolation extrapolationAfter;
Float time;
Float expectedValue, expectedValueStrict;
std::size_t expectedHint;
} AtData[] {
{"before default-constructed",
Extrapolation::DefaultConstructed, Extrapolation::Extrapolated,
-1.0f, 0.0f, 4.0f, 0},
{"before constant",
Extrapolation::Constant, Extrapolation::Extrapolated,
-1.0f, 3.0f, 4.0f, 0},
{"before extrapolated",
Extrapolation::Extrapolated, Extrapolation::DefaultConstructed,
-1.0f, 4.0f, 4.0f, 0},
{"during first",
Extrapolation::DefaultConstructed, Extrapolation::DefaultConstructed,
1.5f, 1.5f, 1.5f, 0},
{"during second",
Extrapolation::DefaultConstructed, Extrapolation::DefaultConstructed,
4.75f, 1.0f, 1.0f, 2},
{"after default-constructed",
Extrapolation::Extrapolated, Extrapolation::DefaultConstructed,
6.0f, 0.0f, -1.5f, 2},
{"after constant",
Extrapolation::Extrapolated, Extrapolation::Constant,
6.0f, 0.5f, -1.5f, 2},
{"after extrapolated",
Extrapolation::DefaultConstructed, Extrapolation::Extrapolated,
6.0f, -1.5f, -1.5f, 2}
};
}
TrackViewTest::TrackViewTest() {
addTests({&TrackViewTest::construct,
&TrackViewTest::constructDefaults,
&TrackViewTest::constructSingleArray,
&TrackViewTest::constructSingleArrayDefaults});
addInstancedTests({&TrackViewTest::at,
&TrackViewTest::atStrict}, Containers::arraySize(AtData));
}
using namespace Math::Literals;
void TrackViewTest::construct() {
constexpr Float keys[]{0.0f, 5.0f};
constexpr Vector3 values[]{{3.0f, 1.0f, 0.1f}, {0.3f, 0.6f, 1.0f}};
const TrackView<Float, Vector3> a{keys, values, Math::lerp,
Extrapolation::Extrapolated, Extrapolation::DefaultConstructed};
CORRADE_COMPARE(a.interpolator(), Math::lerp);
CORRADE_COMPARE(a.before(), Extrapolation::Extrapolated);
CORRADE_COMPARE(a.after(), Extrapolation::DefaultConstructed);
CORRADE_COMPARE(a.keys().size(), 2);
CORRADE_COMPARE(a.values().size(), 2);
CORRADE_COMPARE(a[1], (std::pair<Float, Vector3>{5.0f, {0.3f, 0.6f, 1.0f}}));
}
void TrackViewTest::constructDefaults() {
constexpr Float keys[]{0.0f, 5.0f};
constexpr Vector3 values[]{{3.0f, 1.0f, 0.1f}, {0.3f, 0.6f, 1.0f}};
const TrackView<Float, Vector3> a{keys, values, Math::lerp,
Extrapolation::Constant};
CORRADE_COMPARE(a.interpolator(), Math::lerp);
CORRADE_COMPARE(a.before(), Extrapolation::Constant);
CORRADE_COMPARE(a.after(), Extrapolation::Constant);
CORRADE_COMPARE(a.keys().size(), 2);
CORRADE_COMPARE(a.values().size(), 2);
CORRADE_COMPARE(a[1], (std::pair<Float, Vector3>{5.0f, {0.3f, 0.6f, 1.0f}}));
}
void TrackViewTest::constructSingleArray() {
const std::pair<Float, Vector3> data[]{
{0.0f, {3.0f, 1.0f, 0.1f}},
{5.0f, {0.3f, 0.6f, 1.0f}}};
const TrackView<Float, Vector3> a{data, Math::lerp,
Extrapolation::Extrapolated, Extrapolation::DefaultConstructed};
CORRADE_COMPARE(a.interpolator(), Math::lerp);
CORRADE_COMPARE(a.before(), Extrapolation::Extrapolated);
CORRADE_COMPARE(a.after(), Extrapolation::DefaultConstructed);
CORRADE_COMPARE(a.keys().size(), 2);
CORRADE_COMPARE(a.values().size(), 2);
CORRADE_COMPARE(a[1], (std::pair<Float, Vector3>{5.0f, {0.3f, 0.6f, 1.0f}}));
}
void TrackViewTest::constructSingleArrayDefaults() {
const std::pair<Float, Vector3> data[]{
{0.0f, {3.0f, 1.0f, 0.1f}},
{5.0f, {0.3f, 0.6f, 1.0f}}};
const TrackView<Float, Vector3> a{data, Math::lerp,
Extrapolation::Constant};
CORRADE_COMPARE(a.interpolator(), Math::lerp);
CORRADE_COMPARE(a.before(), Extrapolation::Constant);
CORRADE_COMPARE(a.after(), Extrapolation::Constant);
CORRADE_COMPARE(a.keys().size(), 2);
CORRADE_COMPARE(a.values().size(), 2);
CORRADE_COMPARE(a[1], (std::pair<Float, Vector3>{5.0f, {0.3f, 0.6f, 1.0f}}));
}
namespace {
const std::pair<Float, Float> Keyframes[]{
{0.0f, 3.0f},
{2.0f, 1.0f},
{4.0f, 2.5f},
{5.0f, 0.5f}};
}
void TrackViewTest::at() {
const auto& data = AtData[testCaseInstanceId()];
setTestCaseDescription(data.name);
const TrackView<Float, Float> a{Keyframes, Math::lerp,
data.extrapolationBefore, data.extrapolationAfter};
std::size_t hint{};
CORRADE_COMPARE(a.at(data.time, hint), data.expectedValue);
CORRADE_COMPARE(a.at(data.time), data.expectedValue);
CORRADE_COMPARE(hint, data.expectedHint);
}
void TrackViewTest::atStrict() {
const auto& data = AtData[testCaseInstanceId()];
setTestCaseDescription(data.name);
const TrackView<Float, Float> a{Keyframes, Math::lerp,
data.extrapolationBefore, data.extrapolationAfter};
std::size_t hint{};
CORRADE_COMPARE(a.atStrict(data.time, hint), data.expectedValueStrict);
CORRADE_COMPARE(hint, data.expectedHint);
}
}}}
CORRADE_TEST_MAIN(Magnum::Animation::Test::TrackViewTest)

390
src/Magnum/Animation/Track.h

@ -0,0 +1,390 @@
#ifndef Magnum_Animation_Track_h
#define Magnum_Animation_Track_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
/** @file
* @brief Class @ref Magnum::Animation::Track, @ref Magnum::Animation::TrackView
*/
#include <Corrade/Containers/Array.h>
#include "Magnum/Animation/Animation.h"
#include "Magnum/Animation/Interpolation.h"
namespace Magnum { namespace Animation {
/**
@brief Animation track
@tparam K Key type
@tparam V Value type
Immutable storage of keyframe + value pairs.
@section Animation-Track-usage Basic usage
Animation track is defined by a list of keyframes (time+value pairs),
interpolator function and extrapolation behavior.
@snippet MagnumAnimation.cpp Track-usage
@section Animation-Track-interpolators Types and interpolators
The track supports arbitrary types for keys, values and interpolators. These
are common combinations:
@m_class{m-fullwidth}
Interpolation type | Value type | Result type | Interpolator
------------------- | ----------------- | ------------- | ------------
Constant | any `V` | `V` | @ref Math::select()
Linear | @cpp bool @ce <b></b> | @cpp bool @ce <b></b> | @ref Math::select()
Linear | @ref Math::BoolVector | @ref Math::BoolVector | @ref Math::select()
Linear | any scalar `V` | `V` | @ref Math::lerp()
Linear | any vector `V` | `V` | @ref Math::lerp()
Linear | @ref Math::Quaternion | @ref Math::Quaternion | @ref Math::lerp(const Quaternion<T>&, const Quaternion<T>&, T) "Math::lerp()"
Spherical linear | @ref Math::Quaternion | @ref Math::Quaternion | @ref Math::slerp(const Quaternion<T>&, const Quaternion<T>&, T) "Math::slerp()"
Screw linear | @ref Math::DualQuaternion | @ref Math::DualQuaternion | @ref Math::sclerp(const DualQuaternion<T>&, const DualQuaternion<T>&, T) "Math::sclerp()"
@section Animation-Track-performance Performance tuning
The snippet shown above is convenience-oriented at a cost of sacrificing some
performance. You have the following options:
@subsection Animation-Track-performance-hint Keyframe hinting
The @ref Track and @ref TrackView classes are fully stateless and the
@ref at(K) const function performs a linear search for matching keyframe from
the beginning every time. You can use @ref at(K, std::size_t&) const to
remember last used keyframe index and pass it in the next iteration as a hint:
@snippet MagnumAnimation.cpp Track-performance-hint
@subsection Animation-Track-performance-strict Strict interpolation
While it's possible to have different @ref Extrapolation modes for frames
outside of the track range with graceful handling of single- or zero-frame
animations, the additional checks have some impact. The @ref atStrict() has
implicit @ref Extrapolation::Extrapolated behavior and assumes there are always
at least two keyframes, resulting in more compact interpolation code. If your
animation data satisfy the prerequisites, simply use it in place of @ref at():
@snippet MagnumAnimation.cpp Track-performance-strict
@subsection Animation-Track-performance-cache Cache-efficient data layout
Usually multiple tracks (translation, rotation, scaling) are combined together
to form a single animation. In order to achieve better data layout, consider
interleaving the data and passing them using
@ref Corrade::Containers::StridedArrayView to multiple @ref TrackView
instead of having data duplicated scattered across disjoint allocations of
@ref Track instances:
@snippet MagnumAnimation.cpp Track-performance-cache
@experimental
*/
template<class K, class V> class Track {
public:
/** @brief Key type */
typedef K KeyType;
/** @brief Value type */
typedef V ValueType;
/** @brief Animation result type */
typedef ResultOf<V> ResultType;
/** @brief Interpolation function */
typedef ResultType(*Interpolator)(const ValueType&, const ValueType&, Float);
/**
* @brief Constructor
* @param data Keyframe data
* @param interpolator Interpolator function
* @param before Extrapolation behavior
* @param after Extrapolation behavior after
*
* The keyframe data are assumed to be stored in sorted order. It's not
* an error to have two successive keyframes with the same frame value.
*/
explicit Track(Containers::Array<std::pair<K, V>>&& data, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: _data{std::move(data)}, _interpolator{interpolator}, _before{before}, _after{after} {}
/** @overload */
explicit Track(std::initializer_list<std::pair<K, V>> data, Interpolator interpolator, Extrapolation before, Extrapolation after): Track<K, V>{Containers::Array<std::pair<K, V>>{Containers::InPlaceInit, data}, interpolator, before, after} {}
/** @overload
* Equivalent to calling @ref Track(Containers::Array<std::pair<K, V>>&&, Interpolator, Extrapolation, Extrapolation)
* with both @p before and @p after set to @p extrapolation.
*/
explicit Track(Containers::Array<std::pair<K, V>>&& data, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Constant) noexcept: Track<K, V>{std::move(data), interpolator, extrapolation, extrapolation} {}
/** @overload */
explicit Track(std::initializer_list<std::pair<K, V>> data, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Constant): Track<K, V>{Containers::Array<std::pair<K, V>>{Containers::InPlaceInit, data}, interpolator, extrapolation} {}
/** @brief Copying is not allowed */
Track(const Track<K, V>&) = delete;
/** @brief Move constructor */
Track(Track<K, V>&&) = default;
/** @brief Copying is not allowed */
Track<K, V>& operator=(const Track<K, V>&) = delete;
/** @brief Move constructor */
Track<K, V>& operator=(Track<K, V>&&) = default;
/** @brief Conversion to a view */
operator TrackView<K, V>() const noexcept {
return TrackView<K, V>{_data, _interpolator, _before, _after};
}
/** @brief Interpolation function */
Interpolator interpolator() const { return _interpolator; }
/**
* @brief Extrapolation behavior before first keyframe
*
* @see @ref after(), @ref at()
*/
Extrapolation before() const { return _before; }
/**
* @brief Extrapolation behavior after last keyframe
*
* @see @ref before(), @ref at()
*/
Extrapolation after() const { return _after; }
/**
* @brief Keyframe data
*
* @see @ref keys(), @ref values(), @ref operator[]()
*/
Containers::ArrayView<const std::pair<K, V>> data() const { return _data; }
/**
* @brief Key data
*
* @see @ref data(), @ref values(), @ref operator[]()
*/
Containers::StridedArrayView<const K> keys() const {
return _data ? Containers::StridedArrayView<const K>{&_data[0].first, _data.size(), sizeof(std::pair<K, V>)} : nullptr;
}
/**
* @brief Value data
*
* @see @ref data(), @ref keys(), @ref operator[]()
*/
Containers::StridedArrayView<const V> values() const {
return _data ? Containers::StridedArrayView<const V>{&_data[0].second, _data.size(), sizeof(std::pair<K, V>)} : nullptr;
}
/** @brief Keyframe access */
const std::pair<K, V>& operator[](std::size_t i) const { return _data[i]; }
/**
* @brief Animated value at a given time
*
* Calls @ref interpolate(), see its documentation for more
* information. Note that this function performs a linear search every
* time, use @ref at(K, std::size_t&) const to supply a search hint.
* @see @ref atStrict()
*/
ResultOf<V> at(K frame) const {
std::size_t hint{};
return at(frame, hint);
}
/**
* @brief Animated value at a given time
*
* Calls @ref interpolate(), see its documentation for more
* information.
* @see @ref at(K) const, @ref atStrict(K, std::size_t&) const
*/
ResultOf<V> at(K frame, std::size_t& hint) const {
return interpolate(keys(), values(), _before, _after, _interpolator, frame, hint);
}
/**
* @brief Animated value at a given time
*
* A faster version of @ref at(K, std::size_t&) const with some
* restrictions. Calls @ref interpolateStrict(), see its documentation
* for more information.
*/
ResultOf<V> atStrict(K frame, std::size_t& hint) const {
return interpolateStrict(keys(), values(), _interpolator, frame, hint);
}
private:
Containers::Array<std::pair<K, V>> _data;
Interpolator _interpolator;
Extrapolation _before, _after;
};
/**
@brief Animation track view
@tparam K Key type
@tparam V Value type
Unlike @ref Track this is a non-owning view onto keyframe + value pairs. See
its documentation for more information.
@experimental
*/
template<class K, class V> class TrackView {
public:
/** @brief Key type */
typedef K KeyType;
/** @brief Value type */
typedef V ValueType;
/** @brief Animation result type */
typedef ResultOf<V> ResultType;
/** @brief Interpolation function */
typedef ResultType(*Interpolator)(const ValueType&, const ValueType&, Float);
/**
* @brief Constructor
* @param keys Frame keys
* @param values Frame values
* @param interpolator Interpolation function
* @param before Extrapolation behavior before
* @param after Extrapolation behavior after
*
* The keyframe data are assumed to be stored in sorted order. It's not
* an error to have two successive keyframes with the same frame value.
*/
constexpr explicit TrackView(const Containers::StridedArrayView<const K>& keys, const Containers::StridedArrayView<const V>& values, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: _keys{keys}, _values{values}, _interpolator{interpolator}, _before{before}, _after{after} {}
/** @overload
* Equivalent to calling @ref TrackView(const Containers::StridedArrayView<const K>&, const Containers::StridedArrayView<const V>&, Interpolator, Extrapolation, Extrapolation)
* with both @p before and @p after set to @p extrapolation.
*/
constexpr explicit TrackView(const Containers::StridedArrayView<const K>& keys, const Containers::StridedArrayView<const V>& values, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Extrapolated) noexcept: TrackView<K, V>{keys, values, interpolator, extrapolation, extrapolation} {}
/**
* @brief Construct from an interleaved array
* @param data Keyframe data
* @param interpolator Interpolation function
* @param before Extrapolation behavior before
* @param after Extrapolation behavior after
*
* Converts @p data to a pair of strided array views and calls
* @ref TrackView(const Containers::StridedArrayView<const K>&, const Containers::StridedArrayView<const V>&, Interpolator, Extrapolation, Extrapolation).
*/
constexpr explicit TrackView(Containers::ArrayView<const std::pair<K, V>> data, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: _keys{data ? &data[0].first : nullptr, data.size(), sizeof(std::pair<K, V>)}, _values{data ? &data[0].second : nullptr, data.size(), sizeof(std::pair<K, V>)}, _interpolator{interpolator}, _before{before}, _after{after} {}
/** @overload
* Equivalent to calling @ref TrackView(Containers::ArrayView<const std::pair<K, V>>, Interpolator, Extrapolation, Extrapolation)
* with both @p before and @p after set to @p extrapolation.
*/
constexpr explicit TrackView(Containers::ArrayView<const std::pair<K, V>> data, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Extrapolated) noexcept: TrackView<K, V>{data, interpolator, extrapolation, extrapolation} {}
/**
* @brief Extrapolation behavior before first keyframe
*
* @see @ref after(), @ref at(), @ref atStrict()
*/
Extrapolation before() const { return _before; }
/**
* @brief Extrapolation behavior after last keyframe
*
* @see @ref before(), @ref at(), @ref atStrict()
*/
Extrapolation after() const { return _after; }
/** @brief Interpolation function */
Interpolator interpolator() const { return _interpolator; }
/**
* @brief Key data
*
* @see @ref values(), @ref operator[]()
*/
Containers::StridedArrayView<const K> keys() const { return _keys; }
/**
* @brief Value data
*
* @see @ref keys(), @ref operator[]()
*/
Containers::StridedArrayView<const V> values() const { return _values; }
/** @brief Keyframe access */
std::pair<K, V> operator[](std::size_t i) const {
return {_keys[i], _values[i]};
}
/**
* @brief Animated value at a given time
*
* Calls @ref interpolate(), see its documentation for more
* information. Note that this function performs a linear search every
* time, use @ref at(K, std::size_t&) const to supply a search hint.
* @see @ref atStrict(K, std::size_t&) const
*/
ResultOf<V> at(K frame) const {
std::size_t hint{};
return at(frame, hint);
}
/**
* @brief Animated value at a given time
*
* Calls @ref interpolate(), see its documentation for more
* information.
* @see @ref at(K) const, @ref atStrict(K, std::size_t&) const
*/
ResultOf<V> at(K frame, std::size_t& hint) const {
return interpolate(_keys, _values, _before, _after, _interpolator, frame, hint);
}
/**
* @brief Animated value at a given time
*
* A faster version of @ref at(K, std::size_t&) const with some
* restrictions. Calls @ref interpolateStrict(), see its documentation
* for more information.
*/
ResultOf<V> atStrict(K frame, std::size_t& hint) const {
return interpolateStrict(_keys, _values, _interpolator, frame, hint);
}
private:
Containers::StridedArrayView<const K> _keys;
Containers::StridedArrayView<const V> _values;
Interpolator _interpolator;
Extrapolation _before, _after;
};
}}
#endif
Loading…
Cancel
Save