From fa449c9ad06b01ca5ee416af04807a3aa15cd503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 12 Jul 2018 02:35:44 +0200 Subject: [PATCH] Animation: make it possible to specify Track interpolator via an enum. Or via an enum + func ptr. Main goal of this is to provide a hint to users who want to supply their own interpolator (for example with a different perf/correctness tradeoff, or a optimized/inlined/batch version etc.). --- src/Magnum/Animation/Test/TrackTest.cpp | 203 +++++++++++- src/Magnum/Animation/Test/TrackViewTest.cpp | 193 ++++++++++-- src/Magnum/Animation/Track.h | 326 ++++++++++++++++++-- 3 files changed, 665 insertions(+), 57 deletions(-) diff --git a/src/Magnum/Animation/Test/TrackTest.cpp b/src/Magnum/Animation/Test/TrackTest.cpp index ecbbc8738..dd64ddf64 100644 --- a/src/Magnum/Animation/Test/TrackTest.cpp +++ b/src/Magnum/Animation/Test/TrackTest.cpp @@ -35,10 +35,18 @@ struct TrackTest: TestSuite::Tester { explicit TrackTest(); void constructEmpty(); - void constructArray(); - void constructArrayDefaults(); - void constructInitializerList(); - void constructInitializerListDefaults(); + void constructArrayInterpolator(); + void constructArrayInterpolatorDefaults(); + void constructArrayInterpolation(); + void constructArrayInterpolationDefaults(); + void constructArrayInterpolationInterpolator(); + void constructArrayInterpolationInterpolationDefaults(); + void constructInitializerListInterpolation(); + void constructInitializerListInterpolationDefaults(); + void constructInitializerListInterpolator(); + void constructInitializerListInterpolatorDefaults(); + void constructInitializerListInterpolationInterpolator(); + void constructInitializerListInterpolationInterpolatorDefaults(); void convertView(); @@ -89,10 +97,18 @@ const struct { TrackTest::TrackTest() { addTests({&TrackTest::constructEmpty, - &TrackTest::constructArray, - &TrackTest::constructArrayDefaults, - &TrackTest::constructInitializerList, - &TrackTest::constructInitializerListDefaults, + &TrackTest::constructArrayInterpolator, + &TrackTest::constructArrayInterpolatorDefaults, + &TrackTest::constructArrayInterpolation, + &TrackTest::constructArrayInterpolationDefaults, + &TrackTest::constructArrayInterpolationInterpolator, + &TrackTest::constructArrayInterpolationInterpolationDefaults, + &TrackTest::constructInitializerListInterpolator, + &TrackTest::constructInitializerListInterpolatorDefaults, + &TrackTest::constructInitializerListInterpolation, + &TrackTest::constructInitializerListInterpolationDefaults, + &TrackTest::constructInitializerListInterpolationInterpolator, + &TrackTest::constructInitializerListInterpolationInterpolatorDefaults, &TrackTest::convertView}); @@ -114,13 +130,14 @@ void TrackTest::constructEmpty() { CORRADE_COMPARE(a.at(42.0f), Vector3{}); } -void TrackTest::constructArray() { +void TrackTest::constructArrayInterpolator() { const Track a{ Containers::Array>{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.interpolation(), Interpolation::Custom); CORRADE_COMPARE(a.interpolator(), Math::select); CORRADE_COMPARE(a.before(), Extrapolation::Extrapolated); CORRADE_COMPARE(a.after(), Extrapolation::Constant); @@ -133,12 +150,13 @@ void TrackTest::constructArray() { CORRADE_COMPARE(a.values()[0], (Vector3{3.0f, 1.0f, 0.1f})); } -void TrackTest::constructArrayDefaults() { +void TrackTest::constructArrayInterpolatorDefaults() { const Track a{ Containers::Array>{Containers::InPlaceInit, {{0.0f, {3.0f, 1.0f, 0.1f}}}}, Math::lerp, Extrapolation::DefaultConstructed}; + CORRADE_COMPARE(a.interpolation(), Interpolation::Custom); CORRADE_COMPARE(a.interpolator(), Math::lerp); CORRADE_COMPARE(a.before(), Extrapolation::DefaultConstructed); CORRADE_COMPARE(a.after(), Extrapolation::DefaultConstructed); @@ -151,12 +169,95 @@ void TrackTest::constructArrayDefaults() { CORRADE_COMPARE(a.values()[0], (Vector3{3.0f, 1.0f, 0.1f})); } -void TrackTest::constructInitializerList() { +void TrackTest::constructArrayInterpolation() { + const Track a{ + Containers::Array>{Containers::InPlaceInit, + {{0.0f, {3.0f, 1.0f, 0.1f}}, + {5.0f, {0.3f, 0.6f, 1.0f}}}}, + Interpolation::Linear, Extrapolation::Extrapolated, Extrapolation::Constant}; + + CORRADE_COMPARE(a.interpolation(), Interpolation::Linear); + CORRADE_COMPARE(a.interpolator(), Math::lerp); + 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{5.0f, {0.3f, 0.6f, 1.0f}})); + CORRADE_COMPARE(a.data()[1], (std::pair{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::constructArrayInterpolationDefaults() { + const Track a{ + Containers::Array>{Containers::InPlaceInit, + {{0.0f, {3.0f, 1.0f, 0.1f}}}}, + Interpolation::Constant, Extrapolation::DefaultConstructed}; + + CORRADE_COMPARE(a.interpolation(), Interpolation::Constant); + CORRADE_COMPARE(a.interpolator(), Math::select); + 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{0.0f, {3.0f, 1.0f, 0.1f}})); + CORRADE_COMPARE(a.data()[0], (std::pair{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})); +} + +namespace { + Vector3 customLerp(const Vector3&, const Vector3&, Float) { return {}; } +} + +void TrackTest::constructArrayInterpolationInterpolator() { + const Track a{ + Containers::Array>{Containers::InPlaceInit, + {{0.0f, {3.0f, 1.0f, 0.1f}}, + {5.0f, {0.3f, 0.6f, 1.0f}}}}, + Interpolation::Linear, customLerp, Extrapolation::Extrapolated, Extrapolation::Constant}; + + CORRADE_COMPARE(a.interpolation(), Interpolation::Linear); + CORRADE_COMPARE(a.interpolator(), customLerp); + 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{5.0f, {0.3f, 0.6f, 1.0f}})); + CORRADE_COMPARE(a.data()[1], (std::pair{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::constructArrayInterpolationInterpolationDefaults() { + const Track a{ + Containers::Array>{Containers::InPlaceInit, + {{0.0f, {3.0f, 1.0f, 0.1f}}}}, + Interpolation::Constant, customLerp, Extrapolation::DefaultConstructed}; + + CORRADE_COMPARE(a.interpolation(), Interpolation::Constant); + CORRADE_COMPARE(a.interpolator(), customLerp); + 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{0.0f, {3.0f, 1.0f, 0.1f}})); + CORRADE_COMPARE(a.data()[0], (std::pair{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::constructInitializerListInterpolator() { const Track 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.interpolation(), Interpolation::Custom); CORRADE_COMPARE(a.interpolator(), Math::select); CORRADE_COMPARE(a.before(), Extrapolation::Extrapolated); CORRADE_COMPARE(a.after(), Extrapolation::DefaultConstructed); @@ -169,10 +270,11 @@ void TrackTest::constructInitializerList() { CORRADE_COMPARE(a.values()[0], (Vector3{3.0f, 1.0f, 0.1f})); } -void TrackTest::constructInitializerListDefaults() { +void TrackTest::constructInitializerListInterpolatorDefaults() { const Track a{{{0.0f, {3.0f, 1.0f, 0.1f}}}, Math::lerp, Extrapolation::Constant}; + CORRADE_COMPARE(a.interpolation(), Interpolation::Custom); CORRADE_COMPARE(a.interpolator(), Math::lerp); CORRADE_COMPARE(a.before(), Extrapolation::Constant); CORRADE_COMPARE(a.after(), Extrapolation::Constant); @@ -185,14 +287,87 @@ void TrackTest::constructInitializerListDefaults() { CORRADE_COMPARE(a.values()[0], (Vector3{3.0f, 1.0f, 0.1f})); } +void TrackTest::constructInitializerListInterpolation() { + const Track a{ + {{0.0f, {3.0f, 1.0f, 0.1f}}, + {5.0f, {0.3f, 0.6f, 1.0f}}}, + Interpolation::Linear, Extrapolation::Extrapolated, Extrapolation::DefaultConstructed}; + + CORRADE_COMPARE(a.interpolation(), Interpolation::Linear); + CORRADE_COMPARE(a.interpolator(), Math::lerp); + 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{5.0f, {0.3f, 0.6f, 1.0f}})); + CORRADE_COMPARE(a.data()[1], (std::pair{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::constructInitializerListInterpolationDefaults() { + const Track a{{{0.0f, {3.0f, 1.0f, 0.1f}}}, + Interpolation::Constant, Extrapolation::Constant}; + + CORRADE_COMPARE(a.interpolation(), Interpolation::Constant); + CORRADE_COMPARE(a.interpolator(), Math::select); + 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{0.0f, {3.0f, 1.0f, 0.1f}})); + CORRADE_COMPARE(a.data()[0], (std::pair{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::constructInitializerListInterpolationInterpolator() { + const Track a{ + {{0.0f, {3.0f, 1.0f, 0.1f}}, + {5.0f, {0.3f, 0.6f, 1.0f}}}, + Interpolation::Linear, customLerp, Extrapolation::Extrapolated, Extrapolation::DefaultConstructed}; + + CORRADE_COMPARE(a.interpolation(), Interpolation::Linear); + CORRADE_COMPARE(a.interpolator(), customLerp); + 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{5.0f, {0.3f, 0.6f, 1.0f}})); + CORRADE_COMPARE(a.data()[1], (std::pair{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::constructInitializerListInterpolationInterpolatorDefaults() { + const Track a{{{0.0f, {3.0f, 1.0f, 0.1f}}}, + Interpolation::Constant, customLerp, Extrapolation::Constant}; + + CORRADE_COMPARE(a.interpolation(), Interpolation::Constant); + CORRADE_COMPARE(a.interpolator(), customLerp); + 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{0.0f, {3.0f, 1.0f, 0.1f}})); + CORRADE_COMPARE(a.data()[0], (std::pair{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 a{ {{0.0f, {3.0f, 1.0f, 0.1f}}, {5.0f, {0.3f, 0.6f, 1.0f}}}, - Math::select, Extrapolation::Extrapolated, Extrapolation::DefaultConstructed}; + Interpolation::Linear, customLerp, Extrapolation::Extrapolated, Extrapolation::DefaultConstructed}; const TrackView av = a; - CORRADE_COMPARE(av.interpolator(), Math::select); + CORRADE_COMPARE(av.interpolation(), Interpolation::Linear); + CORRADE_COMPARE(av.interpolator(), customLerp); CORRADE_COMPARE(av.before(), Extrapolation::Extrapolated); CORRADE_COMPARE(av.after(), Extrapolation::DefaultConstructed); CORRADE_COMPARE(av.keys().size(), 2); diff --git a/src/Magnum/Animation/Test/TrackViewTest.cpp b/src/Magnum/Animation/Test/TrackViewTest.cpp index baffe25d6..3c026f996 100644 --- a/src/Magnum/Animation/Test/TrackViewTest.cpp +++ b/src/Magnum/Animation/Test/TrackViewTest.cpp @@ -35,10 +35,18 @@ struct TrackViewTest: TestSuite::Tester { explicit TrackViewTest(); void constructEmpty(); - void construct(); - void constructDefaults(); - void constructSingleArray(); - void constructSingleArrayDefaults(); + void constructInterpolator(); + void constructInterpolatorDefaults(); + void constructInterpolation(); + void constructInterpolationDefaults(); + void constructInterpolationInterpolator(); + void constructInterpolationInterpolatorDefaults(); + void constructSingleArrayInterpolator(); + void constructSingleArrayInterpolatorDefaults(); + void constructSingleArrayInterpolation(); + void constructSingleArrayInterpolationDefaults(); + void constructSingleArrayInterpolationInterpolator(); + void constructSingleArrayInterpolationInterpolatorDefaults(); void constructCopyStorage(); @@ -89,10 +97,18 @@ const struct { TrackViewTest::TrackViewTest() { addTests({&TrackViewTest::constructEmpty, - &TrackViewTest::construct, - &TrackViewTest::constructDefaults, - &TrackViewTest::constructSingleArray, - &TrackViewTest::constructSingleArrayDefaults, + &TrackViewTest::constructInterpolator, + &TrackViewTest::constructInterpolatorDefaults, + &TrackViewTest::constructInterpolation, + &TrackViewTest::constructInterpolationDefaults, + &TrackViewTest::constructInterpolationInterpolator, + &TrackViewTest::constructInterpolationInterpolatorDefaults, + &TrackViewTest::constructSingleArrayInterpolator, + &TrackViewTest::constructSingleArrayInterpolatorDefaults, + &TrackViewTest::constructSingleArrayInterpolation, + &TrackViewTest::constructSingleArrayInterpolationDefaults, + &TrackViewTest::constructSingleArrayInterpolationInterpolator, + &TrackViewTest::constructSingleArrayInterpolationInterpolatorDefaults, &TrackViewTest::constructCopyStorage}); @@ -114,14 +130,15 @@ void TrackViewTest::constructEmpty() { CORRADE_COMPARE(a.at(42.0f), Vector3{}); } -void TrackViewTest::construct() { +void TrackViewTest::constructInterpolator() { constexpr Float keys[]{0.0f, 5.0f}; constexpr Vector3 values[]{{3.0f, 1.0f, 0.1f}, {0.3f, 0.6f, 1.0f}}; - const TrackView a{keys, values, Math::lerp, + const TrackView a{keys, values, Math::select, Extrapolation::Extrapolated, Extrapolation::DefaultConstructed}; - CORRADE_COMPARE(a.interpolator(), Math::lerp); + CORRADE_COMPARE(a.interpolation(), Interpolation::Custom); + CORRADE_COMPARE(a.interpolator(), Math::select); CORRADE_COMPARE(a.before(), Extrapolation::Extrapolated); CORRADE_COMPARE(a.after(), Extrapolation::DefaultConstructed); CORRADE_COMPARE(a.keys().size(), 2); @@ -129,13 +146,14 @@ void TrackViewTest::construct() { CORRADE_COMPARE(a[1], (std::pair{5.0f, {0.3f, 0.6f, 1.0f}})); } -void TrackViewTest::constructDefaults() { +void TrackViewTest::constructInterpolatorDefaults() { constexpr Float keys[]{0.0f, 5.0f}; constexpr Vector3 values[]{{3.0f, 1.0f, 0.1f}, {0.3f, 0.6f, 1.0f}}; const TrackView a{keys, values, Math::lerp, Extrapolation::Constant}; + CORRADE_COMPARE(a.interpolation(), Interpolation::Custom); CORRADE_COMPARE(a.interpolator(), Math::lerp); CORRADE_COMPARE(a.before(), Extrapolation::Constant); CORRADE_COMPARE(a.after(), Extrapolation::Constant); @@ -144,15 +162,84 @@ void TrackViewTest::constructDefaults() { CORRADE_COMPARE(a[1], (std::pair{5.0f, {0.3f, 0.6f, 1.0f}})); } -void TrackViewTest::constructSingleArray() { +void TrackViewTest::constructInterpolation() { + constexpr Float keys[]{0.0f, 5.0f}; + constexpr Vector3 values[]{{3.0f, 1.0f, 0.1f}, {0.3f, 0.6f, 1.0f}}; + + const TrackView a{keys, values, Interpolation::Linear, + Extrapolation::Extrapolated, Extrapolation::DefaultConstructed}; + + CORRADE_COMPARE(a.interpolation(), Interpolation::Linear); + 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{5.0f, {0.3f, 0.6f, 1.0f}})); +} + +void TrackViewTest::constructInterpolationDefaults() { + constexpr Float keys[]{0.0f, 5.0f}; + constexpr Vector3 values[]{{3.0f, 1.0f, 0.1f}, {0.3f, 0.6f, 1.0f}}; + + const TrackView a{keys, values, Interpolation::Constant, + Extrapolation::Constant}; + + CORRADE_COMPARE(a.interpolation(), Interpolation::Constant); + CORRADE_COMPARE(a.interpolator(), Math::select); + 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{5.0f, {0.3f, 0.6f, 1.0f}})); +} + +namespace { + Vector3 customLerp(const Vector3&, const Vector3&, Float) { return {}; } +} + +void TrackViewTest::constructInterpolationInterpolator() { + constexpr Float keys[]{0.0f, 5.0f}; + constexpr Vector3 values[]{{3.0f, 1.0f, 0.1f}, {0.3f, 0.6f, 1.0f}}; + + const TrackView a{keys, values, Interpolation::Linear, + customLerp, Extrapolation::Extrapolated, Extrapolation::DefaultConstructed}; + + CORRADE_COMPARE(a.interpolation(), Interpolation::Linear); + CORRADE_COMPARE(a.interpolator(), customLerp); + 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{5.0f, {0.3f, 0.6f, 1.0f}})); +} + +void TrackViewTest::constructInterpolationInterpolatorDefaults() { + constexpr Float keys[]{0.0f, 5.0f}; + constexpr Vector3 values[]{{3.0f, 1.0f, 0.1f}, {0.3f, 0.6f, 1.0f}}; + + const TrackView a{keys, values, Interpolation::Constant, + customLerp, Extrapolation::Constant}; + + CORRADE_COMPARE(a.interpolation(), Interpolation::Constant); + CORRADE_COMPARE(a.interpolator(), customLerp); + 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{5.0f, {0.3f, 0.6f, 1.0f}})); +} + +void TrackViewTest::constructSingleArrayInterpolator() { const std::pair data[]{ {0.0f, {3.0f, 1.0f, 0.1f}}, {5.0f, {0.3f, 0.6f, 1.0f}}}; - const TrackView a{data, Math::lerp, + const TrackView a{data, Math::select, Extrapolation::Extrapolated, Extrapolation::DefaultConstructed}; - CORRADE_COMPARE(a.interpolator(), Math::lerp); + CORRADE_COMPARE(a.interpolation(), Interpolation::Custom); + CORRADE_COMPARE(a.interpolator(), Math::select); CORRADE_COMPARE(a.before(), Extrapolation::Extrapolated); CORRADE_COMPARE(a.after(), Extrapolation::DefaultConstructed); CORRADE_COMPARE(a.keys().size(), 2); @@ -160,7 +247,7 @@ void TrackViewTest::constructSingleArray() { CORRADE_COMPARE(a[1], (std::pair{5.0f, {0.3f, 0.6f, 1.0f}})); } -void TrackViewTest::constructSingleArrayDefaults() { +void TrackViewTest::constructSingleArrayInterpolatorDefaults() { const std::pair data[]{ {0.0f, {3.0f, 1.0f, 0.1f}}, {5.0f, {0.3f, 0.6f, 1.0f}}}; @@ -168,6 +255,7 @@ void TrackViewTest::constructSingleArrayDefaults() { const TrackView a{data, Math::lerp, Extrapolation::Constant}; + CORRADE_COMPARE(a.interpolation(), Interpolation::Custom); CORRADE_COMPARE(a.interpolator(), Math::lerp); CORRADE_COMPARE(a.before(), Extrapolation::Constant); CORRADE_COMPARE(a.after(), Extrapolation::Constant); @@ -176,19 +264,88 @@ void TrackViewTest::constructSingleArrayDefaults() { CORRADE_COMPARE(a[1], (std::pair{5.0f, {0.3f, 0.6f, 1.0f}})); } +void TrackViewTest::constructSingleArrayInterpolation() { + const std::pair data[]{ + {0.0f, {3.0f, 1.0f, 0.1f}}, + {5.0f, {0.3f, 0.6f, 1.0f}}}; + + const TrackView a{data, Interpolation::Linear, + Extrapolation::Extrapolated, Extrapolation::DefaultConstructed}; + + CORRADE_COMPARE(a.interpolation(), Interpolation::Linear); + 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{5.0f, {0.3f, 0.6f, 1.0f}})); +} + +void TrackViewTest::constructSingleArrayInterpolationDefaults() { + const std::pair data[]{ + {0.0f, {3.0f, 1.0f, 0.1f}}, + {5.0f, {0.3f, 0.6f, 1.0f}}}; + + const TrackView a{data, Interpolation::Constant, + Extrapolation::Constant}; + + CORRADE_COMPARE(a.interpolation(), Interpolation::Constant); + CORRADE_COMPARE(a.interpolator(), Math::select); + 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{5.0f, {0.3f, 0.6f, 1.0f}})); +} + +void TrackViewTest::constructSingleArrayInterpolationInterpolator() { + const std::pair data[]{ + {0.0f, {3.0f, 1.0f, 0.1f}}, + {5.0f, {0.3f, 0.6f, 1.0f}}}; + + const TrackView a{data, Interpolation::Linear, customLerp, + Extrapolation::Extrapolated, Extrapolation::DefaultConstructed}; + + CORRADE_COMPARE(a.interpolation(), Interpolation::Linear); + CORRADE_COMPARE(a.interpolator(), customLerp); + 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{5.0f, {0.3f, 0.6f, 1.0f}})); +} + +void TrackViewTest::constructSingleArrayInterpolationInterpolatorDefaults() { + const std::pair data[]{ + {0.0f, {3.0f, 1.0f, 0.1f}}, + {5.0f, {0.3f, 0.6f, 1.0f}}}; + + const TrackView a{data, Interpolation::Constant, + customLerp, Extrapolation::Constant}; + + CORRADE_COMPARE(a.interpolation(), Interpolation::Constant); + CORRADE_COMPARE(a.interpolator(), customLerp); + 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{5.0f, {0.3f, 0.6f, 1.0f}})); +} + void TrackViewTest::constructCopyStorage() { const std::pair data[]{ {0.0f, {3.0f, 1.0f, 0.1f}}, {5.0f, {0.3f, 0.6f, 1.0f}}}; - const TrackView a{data, Math::lerp, + const TrackView a{data, Interpolation::Constant, customLerp, Extrapolation::Extrapolated, Extrapolation::DefaultConstructed}; const TrackViewStorage b = a; auto& bv = *static_cast*>(&b); - CORRADE_COMPARE(bv.interpolator(), Math::lerp); + CORRADE_COMPARE(bv.interpolation(), Interpolation::Constant); + CORRADE_COMPARE(bv.interpolator(), customLerp); CORRADE_COMPARE(bv.before(), Extrapolation::Extrapolated); CORRADE_COMPARE(bv.after(), Extrapolation::DefaultConstructed); CORRADE_COMPARE(bv.keys().size(), 2); diff --git a/src/Magnum/Animation/Track.h b/src/Magnum/Animation/Track.h index 7ec0bfe11..288b25366 100644 --- a/src/Magnum/Animation/Track.h +++ b/src/Magnum/Animation/Track.h @@ -69,6 +69,13 @@ Linear | @ref Math::Quaternion | @ref Math::Quaternion | @ref Math: Spherical linear | @ref Math::Quaternion | @ref Math::Quaternion | @ref Math::slerp(const Quaternion&, const Quaternion&, T) "Math::slerp()" Screw linear | @ref Math::DualQuaternion | @ref Math::DualQuaternion | @ref Math::sclerp(const DualQuaternion&, const DualQuaternion&, T) "Math::sclerp()" +It's also possible to supply a generic interpolation behavior by passing the +@ref Interpolation enum to the constructor. In case the interpolator function +is not passed in as well, it's autodetected using @ref interpolatorFor(). See +its documentation for more information. The @ref Interpolation enum is then +stored in @ref interpolation() and acts as a hint for desired interpolation +behavior for users who might want to use their own interpolator. + @section Animation-Track-performance Performance tuning The snippet shown above is convenience-oriented at a cost of sacrificing some @@ -105,6 +112,20 @@ instead of having data duplicated scattered across disjoint allocations of @snippet MagnumAnimation.cpp Track-performance-cache +@subsection Animation-Track-performance-interpolator Interpolator function choice + +The interpolator function has a direct effect on animation performance. You can +choose a less complex interpolator (constant instead of linear or linear +instead of spheric linear, for example) either during construction or passing +it directly to @ref at() / @ref atStrict(). The @ref interpolator() can act as +a hint on what kind of function should be chosen. Depending on how the track +was constructed, passing the interpolator directly to @ref at() / @ref atStrict() +usually also results in it being inlined by the compiler and thus faster than +an indirect function call. + +Note that when constructing the track by just passing @ref Interpolator to the +constructor, the function is chosen by @ref interpolatorFor(), which favors +correctness over performance. See its documentation for more information. @experimental */ template>&&, Interpolation, Interpolator, Extrapolation, Extrapolation) or + * @ref Track(Containers::Array>&&, Interpolation, Extrapolation, Extrapolation) + * for an alternative. */ - explicit Track(Containers::Array>&& data, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: _data{std::move(data)}, _interpolator{interpolator}, _before{before}, _after{after} {} + explicit Track(Containers::Array>&& data, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: _data{std::move(data)}, _interpolator{interpolator}, _interpolation{Interpolation::Custom}, _before{before}, _after{after} {} /** @overload */ explicit Track(std::initializer_list> data, Interpolator interpolator, Extrapolation before, Extrapolation after): Track{Containers::Array>{Containers::InPlaceInit, data}, interpolator, before, after} {} @@ -158,6 +183,61 @@ template> data, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Constant): Track{Containers::Array>{Containers::InPlaceInit, data}, interpolator, extrapolation, extrapolation} {} + /** + * @brief Construct with both generic and custom interpolator + * @param data Keyframe data + * @param interpolation Interpolation behavior + * @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. + * @p interpolation acts as a behavior hint to users that might want to + * supply their own interpolator function to @ref at() or + * @ref atStrict(). + */ + explicit Track(Containers::Array>&& data, Interpolation interpolation, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: _data{std::move(data)}, _interpolator{interpolator}, _interpolation{interpolation}, _before{before}, _after{after} {} + + /** @overload */ + explicit Track(std::initializer_list> data, Interpolation interpolation, Interpolator interpolator, Extrapolation before, Extrapolation after): Track{Containers::Array>{Containers::InPlaceInit, data}, interpolation, interpolator, before, after} {} + + /** @overload + * Equivalent to calling @ref Track(Containers::Array>&&, Interpolation, Interpolator, Extrapolation, Extrapolation) + * with both @p before and @p after set to @p extrapolation. + */ + explicit Track(Containers::Array>&& data, Interpolation interpolation, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Constant) noexcept: Track{std::move(data), interpolation, interpolator, extrapolation, extrapolation} {} + + /** @overload */ + explicit Track(std::initializer_list> data, Interpolation interpolation, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Constant): Track{Containers::Array>{Containers::InPlaceInit, data}, interpolation, interpolator, extrapolation} {} + + /** + * @brief Construct with generic interpolation behavior + * @param data Keyframe data + * @param interpolation Interpolation behavior + * @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. + * The @ref interpolator() function is autodetected from + * @p interpolation using @ref interpolatorFor(). See its documentation + * for more information. + */ + explicit Track(Containers::Array>&& data, Interpolation interpolation, Extrapolation before, Extrapolation after) noexcept: _data{std::move(data)}, _interpolator{interpolatorFor(interpolation)}, _interpolation{interpolation}, _before{before}, _after{after} {} + + /** @overload */ + explicit Track(std::initializer_list> data, Interpolation interpolation, Extrapolation before, Extrapolation after): Track{Containers::Array>{Containers::InPlaceInit, data}, interpolation, before, after} {} + + /** @overload + * Equivalent to calling @ref Track(Containers::Array>&&, Interpolation, Extrapolation, Extrapolation) + * with both @p before and @p after set to @p extrapolation. + */ + explicit Track(Containers::Array>&& data, Interpolation interpolation, Extrapolation extrapolation = Extrapolation::Constant) noexcept: Track{std::move(data), interpolation, extrapolation, extrapolation} {} + + /** @overload */ + explicit Track(std::initializer_list> data, Interpolation interpolation, Extrapolation extrapolation = Extrapolation::Constant): Track{Containers::Array>{Containers::InPlaceInit, data}, interpolation, extrapolation, extrapolation} {} + /** @brief Copying is not allowed */ Track(const Track&) = delete; @@ -172,10 +252,23 @@ template() const noexcept { - return TrackView{_data, _interpolator, _before, _after}; + return TrackView{_data, _interpolation, _interpolator, _before, _after}; } - /** @brief Interpolation function */ + /** + * @brief Interpolation behavior + * + * Acts as a behavior hint to users that might want to supply their own + * interpolator function to @ref at() or @ref atStrict(). + * @see @ref interpolator() + */ + Interpolation interpolation() const { return _interpolation; } + + /** + * @brief Interpolation function + * + * @see @ref interpolation() + */ Interpolator interpolator() const { return _interpolator; } /** @@ -226,11 +319,12 @@ template> _data; Interpolator _interpolator; + Interpolation _interpolation; Extrapolation _before, _after; }; @@ -269,16 +404,17 @@ Cast to @ref TrackView of correct type to use. */ class TrackViewStorage { public: - constexpr /*implicit*/ TrackViewStorage() noexcept: _keys{}, _values{}, _interpolator{}, _before{}, _after{} {} + constexpr /*implicit*/ TrackViewStorage() noexcept: _keys{}, _values{}, _interpolator{}, _interpolation{}, _before{}, _after{} {} private: template friend class TrackView; - template explicit TrackViewStorage(const Containers::StridedArrayView& keys, const Containers::StridedArrayView& values, R(*interpolator)(const V&, const V&, Float), Extrapolation before, Extrapolation after) noexcept: _keys{reinterpret_cast&>(keys)}, _values{reinterpret_cast&>(values)}, _interpolator{reinterpret_cast(interpolator)}, _before{before}, _after{after} {} + template explicit TrackViewStorage(const Containers::StridedArrayView& keys, const Containers::StridedArrayView& values, Interpolation interpolation, R(*interpolator)(const V&, const V&, Float), Extrapolation before, Extrapolation after) noexcept: _keys{reinterpret_cast&>(keys)}, _values{reinterpret_cast&>(values)}, _interpolator{reinterpret_cast(interpolator)}, _interpolation{interpolation}, _before{before}, _after{after} {} Containers::StridedArrayView _keys; Containers::StridedArrayView _values; void(*_interpolator)(void); + Interpolation _interpolation; Extrapolation _before, _after; }; @@ -320,7 +456,7 @@ template&, const Containers::StridedArrayView&, Interpolation, Interpolator, Extrapolation, Extrapolation) or + * @ref TrackView(const Containers::StridedArrayView&, const Containers::StridedArrayView&, Interpolation, Extrapolation, Extrapolation) + * for an alternative. */ - explicit TrackView(const Containers::StridedArrayView& keys, const Containers::StridedArrayView& values, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: TrackViewStorage{keys, values, interpolator, before, after} {} + explicit TrackView(const Containers::StridedArrayView& keys, const Containers::StridedArrayView& values, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: TrackViewStorage{keys, values, Interpolation::Custom, interpolator, before, after} {} /** @overload * Equivalent to calling @ref TrackView(const Containers::StridedArrayView&, const Containers::StridedArrayView&, Interpolator, Extrapolation, Extrapolation) @@ -339,7 +479,7 @@ template& keys, const Containers::StridedArrayView& values, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Extrapolated) noexcept: TrackView{keys, values, interpolator, extrapolation, extrapolation} {} /** - * @brief Construct from an interleaved array + * @brief Construct with custom interpolator from an interleaved array * @param data Keyframe data * @param interpolator Interpolation function * @param before Extrapolation behavior before @@ -348,7 +488,7 @@ template&, const Containers::StridedArrayView&, Interpolator, Extrapolation, Extrapolation). */ - explicit TrackView(Containers::ArrayView> data, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: TrackViewStorage{Containers::StridedArrayView{data ? &data[0].first : nullptr, data.size(), sizeof(std::pair)}, Containers::StridedArrayView{data ? &data[0].second : nullptr, data.size(), sizeof(std::pair)}, interpolator, before, after} {} + explicit TrackView(Containers::ArrayView> data, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: TrackView{Containers::StridedArrayView{data ? &data[0].first : nullptr, data.size(), sizeof(std::pair)}, Containers::StridedArrayView{data ? &data[0].second : nullptr, data.size(), sizeof(std::pair)}, interpolator, before, after} {} /** @overload * Equivalent to calling @ref TrackView(Containers::ArrayView>, Interpolator, Extrapolation, Extrapolation) @@ -356,6 +496,106 @@ template> data, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Extrapolated) noexcept: TrackView{data, interpolator, extrapolation, extrapolation} {} + /** + * @brief Construct with both generic and custom interpolator + * @param keys Frame keys + * @param values Frame values + * @param interpolation Interpolation behavior + * @param interpolator Interpolator 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. + * @p interpolation acts as a behavior hint to users that might want to + * supply their own interpolator function to @ref at() or + * @ref atStrict(). + */ + explicit TrackView(const Containers::StridedArrayView& keys, const Containers::StridedArrayView& values, Interpolation interpolation, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: TrackViewStorage{keys, values, interpolation, interpolator, before, after} {} + + /** @overload + * Equivalent to calling @ref TrackView(const Containers::StridedArrayView&, const Containers::StridedArrayView&, Interpolation, Interpolator, Extrapolation, Extrapolation) + * with both @p before and @p after set to @p extrapolation. + */ + explicit TrackView(const Containers::StridedArrayView& keys, const Containers::StridedArrayView& values, Interpolation interpolation, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Extrapolated) noexcept: TrackView{keys, values, interpolation, interpolator, extrapolation, extrapolation} {} + + /** + * @brief Construct with both generic and custom interpolator from an interleaved array + * @param data Keyframe data + * @param interpolation Interpolation behavior + * @param interpolator Interpolator 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 Containers::StridedArrayView&, Interpolator, Extrapolation, Extrapolation). + */ + explicit TrackView(Containers::ArrayView> data, Interpolation interpolation, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: TrackViewStorage{Containers::StridedArrayView{data ? &data[0].first : nullptr, data.size(), sizeof(std::pair)}, Containers::StridedArrayView{data ? &data[0].second : nullptr, data.size(), sizeof(std::pair)}, interpolation, interpolator, before, after} {} + + /** @overload + * Equivalent to calling @ref TrackView(Containers::ArrayView>, Interpolation, Interpolator, Extrapolation, Extrapolation) + * with both @p before and @p after set to @p extrapolation. + */ + explicit TrackView(Containers::ArrayView> data, Interpolation interpolation, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Extrapolated) noexcept: TrackView{data, interpolation, interpolator, extrapolation, extrapolation} {} + + /** + * @brief Construct with generic interpolation behavior + * @param keys Frame keys + * @param values Frame values + * @param interpolation Interpolation behavior + * @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. + * The @ref interpolator() function is autodetected from + * @p interpolation using @ref interpolatorFor(). See its documentation + * for more information. + */ + explicit TrackView(const Containers::StridedArrayView& keys, const Containers::StridedArrayView& values, Interpolation interpolation, Extrapolation before, Extrapolation after) noexcept: TrackViewStorage{keys, values, interpolation, interpolatorFor(interpolation), before, after} {} + + /** @overload + * Equivalent to calling @ref TrackView(const Containers::StridedArrayView&, const Containers::StridedArrayView&, Interpolation, Extrapolation, Extrapolation) + * with both @p before and @p after set to @p extrapolation. + */ + explicit TrackView(const Containers::StridedArrayView& keys, const Containers::StridedArrayView& values, Interpolation interpolation, Extrapolation extrapolation = Extrapolation::Extrapolated) noexcept: TrackView{keys, values, interpolation, extrapolation, extrapolation} {} + + /** + * @brief Construct with generic interpolation behavior from an interleaved array + * @param data Keyframe data + * @param interpolation Interpolation behavior + * @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 Containers::StridedArrayView&, Interpolator, Extrapolation, Extrapolation). + */ + explicit TrackView(Containers::ArrayView> data, Interpolation interpolation, Extrapolation before, Extrapolation after) noexcept: TrackView{Containers::StridedArrayView{data ? &data[0].first : nullptr, data.size(), sizeof(std::pair)}, Containers::StridedArrayView{data ? &data[0].second : nullptr, data.size(), sizeof(std::pair)}, interpolation, before, after} {} + + /** @overload + * Equivalent to calling @ref TrackView(Containers::ArrayView>, Interpolation, Extrapolation, Extrapolation) + * with both @p before and @p after set to @p extrapolation. + */ + explicit TrackView(Containers::ArrayView> data, Interpolation interpolation, Extrapolation extrapolation = Extrapolation::Extrapolated) noexcept: TrackView{data, interpolation, extrapolation, extrapolation} {} + + /** + * @brief Interpolation behavior + * + * Acts as a behavior hint to users that might want to supply their own + * interpolator function to @ref at() or @ref atStrict(). + * @see @ref interpolator() + */ + Interpolation interpolation() const { return _interpolation; } + + /** + * @brief Interpolation function + * + * @see @ref interpolation() + */ + Interpolator interpolator() const { + return reinterpret_cast(_interpolator); + } + /** * @brief Extrapolation behavior before first keyframe * @@ -370,11 +610,6 @@ template(_interpolator); - } - /** * @brief Key data * @@ -404,11 +639,12 @@ template