From 0fd62194c8e18a7c77f3f1050996292aea35fb9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 11 Nov 2019 23:21:02 +0100 Subject: [PATCH] Trade: mutable access in AnimationData. Follows the change done in MeshData. --- doc/changelog.dox | 9 +- doc/snippets/MagnumTrade.cpp | 17 ++ src/Magnum/Trade/AnimationData.cpp | 29 ++- src/Magnum/Trade/AnimationData.h | 132 ++++++++++++-- src/Magnum/Trade/Data.h | 7 +- src/Magnum/Trade/Test/AnimationDataTest.cpp | 188 +++++++++++++++++++- 6 files changed, 362 insertions(+), 20 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index bcc9c3d08..e3e87a559 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -168,6 +168,11 @@ See also: @ref Trade::AbstractImporter::image2D(), @ref Trade::AbstractImporter::image2DLevelCount() and similar APIs for 1D and 3D images +- The @ref Trade::AnimationData class received support for mutable data + access with new constructors and the + @ref Trade::AnimationData::mutableData() "mutableData()" and + @ref Trade::AnimationData::mutableTrack() "mutableTrack()" accessors. See + @ref Trade-AnimationData-usage-mutable for more information. @subsubsection changelog-latest-new-vk Vk library @@ -391,7 +396,9 @@ See also: values it references. Existing code needs to be changed to say @cpp TrackView @ce instead of @cpp TrackView @ce. Following this change, @ref Trade::AnimationData now also return instances - with @cpp const @ce types. + with @cpp const @ce types and the non-const + @ref Trade::AnimationData::data() was renamed to + @ref Trade::AnimationData::mutableData() "mutableData()". - The 4-argument @ref GL::DynamicAttribute constructor was not marked as @cpp explicit @ce by mistake, it's done now to enforce readability in long expressions. diff --git a/doc/snippets/MagnumTrade.cpp b/doc/snippets/MagnumTrade.cpp index 82296fae2..922237381 100644 --- a/doc/snippets/MagnumTrade.cpp +++ b/doc/snippets/MagnumTrade.cpp @@ -174,6 +174,23 @@ Containers::Array animationData = data->release(); /* Take ownership */ /* [AnimationData-usage] */ } +{ +Trade::AnimationData data{nullptr, {}}; +/* [AnimationData-usage-mutable] */ +for(UnsignedInt i = 0; i != data.trackCount(); ++i) { + if(data.trackTargetType(i) != Trade::AnimationTrackTargetType::Translation3D) + continue; + /* Check prerequisites */ + if(!(data.dataFlags() & Trade::DataFlag::Mutable) || + data.trackType(i) != Trade::AnimationTrackType::Vector2) + Fatal{} << "Oops"; + + MeshTools::transformVectorsInPlace(Matrix4::scaling(Vector3::yScale(-1.0f)), + data.mutableTrack(i).values()); +} +/* [AnimationData-usage-mutable] */ +} + { /* [ImageData-construction] */ Containers::Array data; diff --git a/src/Magnum/Trade/AnimationData.cpp b/src/Magnum/Trade/AnimationData.cpp index 612a3005f..5bfbfba50 100644 --- a/src/Magnum/Trade/AnimationData.cpp +++ b/src/Magnum/Trade/AnimationData.cpp @@ -32,9 +32,15 @@ namespace Magnum { namespace Trade { -AnimationData::AnimationData(Containers::Array&& data, Containers::Array&& tracks, const Range1D& duration, const void* importerState) noexcept: _duration{duration}, _data{std::move(data)}, _tracks{std::move(tracks)}, _importerState{importerState} {} +AnimationData::AnimationData(Containers::Array&& data, Containers::Array&& tracks, const Range1D& duration, const void* importerState) noexcept: _dataFlags{DataFlag::Owned|DataFlag::Mutable}, _duration{duration}, _data{std::move(data)}, _tracks{std::move(tracks)}, _importerState{importerState} {} -AnimationData::AnimationData(Containers::Array&& data, Containers::Array&& tracks, const void* importerState) noexcept: _data{std::move(data)}, _tracks{std::move(tracks)}, _importerState{importerState} { +AnimationData::AnimationData(const DataFlags dataFlags, const Containers::ArrayView data, Containers::Array&& tracks, const Range1D& duration, const void* importerState) noexcept: AnimationData{Containers::Array{const_cast(static_cast(data.data())), data.size(), Implementation::nonOwnedArrayDeleter}, std::move(tracks), duration, importerState} { + CORRADE_ASSERT(!(dataFlags & DataFlag::Owned), + "Trade::AnimationData: can't construct a non-owned instance with" << dataFlags, ); + _dataFlags = dataFlags; +} + +AnimationData::AnimationData(Containers::Array&& data, Containers::Array&& tracks, const void* importerState) noexcept: _dataFlags{DataFlag::Owned|DataFlag::Mutable}, _data{std::move(data)}, _tracks{std::move(tracks)}, _importerState{importerState} { if(!_tracks.empty()) { /* Reset duration to duration of the first track so it properly support cases where tracks don't start at 0 */ @@ -44,12 +50,24 @@ AnimationData::AnimationData(Containers::Array&& data, Containers::Array data, Containers::Array&& tracks, const void* importerState) noexcept: AnimationData{Containers::Array{const_cast(static_cast(data.data())), data.size(), Implementation::nonOwnedArrayDeleter}, std::move(tracks), importerState} { + CORRADE_ASSERT(!(dataFlags & DataFlag::Owned), + "Trade::AnimationData: can't construct a non-owned instance with" << dataFlags, ); + _dataFlags = dataFlags; +} + AnimationData::~AnimationData() = default; AnimationData::AnimationData(AnimationData&&) noexcept = default; AnimationData& AnimationData::operator=(AnimationData&&) noexcept = default; +Containers::ArrayView AnimationData::mutableData() & { + CORRADE_ASSERT(_dataFlags & DataFlag::Mutable, + "Trade::AnimationData::mutableData(): the animation is not mutable", {}); + return _data; +} + AnimationTrackType AnimationData::trackType(UnsignedInt id) const { CORRADE_ASSERT(id < _tracks.size(), "Trade::AnimationData::trackType(): index out of range", {}); return _tracks[id]._type; @@ -75,6 +93,13 @@ const Animation::TrackViewStorage& AnimationData::track(UnsignedInt return _tracks[id]._view; } +const Animation::TrackViewStorage& AnimationData::mutableTrack(UnsignedInt id) { + CORRADE_ASSERT(_dataFlags & DataFlag::Mutable, + "Trade::AnimationData::mutableTrack(): the animation is not mutable", reinterpret_cast&>(_tracks[id]._view)); + CORRADE_ASSERT(id < _tracks.size(), "Trade::AnimationData::track(): index out of range", reinterpret_cast&>(_tracks[id]._view)); + return reinterpret_cast&>(_tracks[id]._view); +} + template auto animationInterpolatorFor(Animation::Interpolation interpolation) -> R(*)(const V&, const V&, Float) { return Animation::interpolatorFor(interpolation); } diff --git a/src/Magnum/Trade/AnimationData.h b/src/Magnum/Trade/AnimationData.h index 74ad5db9c..4ca38958d 100644 --- a/src/Magnum/Trade/AnimationData.h +++ b/src/Magnum/Trade/AnimationData.h @@ -32,6 +32,7 @@ #include "Magnum/Magnum.h" #include "Magnum/Math/Math.h" #include "Magnum/Animation/Track.h" +#include "Magnum/Trade/Data.h" #include "Magnum/Trade/Trade.h" #include "Magnum/Trade/visibility.h" @@ -278,12 +279,26 @@ array is then updated during calls to @ref Animation::Player::advance(). It's also possible to directly update object transformations using callbacks, among other things. See documentation of the @ref Animation::Player class for more information. + +@section Trade-AnimationData-usage-mutable Mutable data access + +The interfaces implicitly provide @cpp const @ce views on the contained +keyframe data through the @ref data() and @ref track() accessors. This is done +because in general case the data can also refer to a memory-mapped file or +constant memory. In cases when it's desirable to modify the data in-place, +there's the @ref mutableData() and @ref mutableTrack() set of functions. To use +these, you need to check that the data are mutable using @ref dataFlags() +first. The following snippet inverts the Y coordinate of a translation +animation: + +@snippet MagnumTrade.cpp AnimationData-usage-mutable + @experimental */ class MAGNUM_TRADE_EXPORT AnimationData { public: /** - * @brief Construct with implicit duration + * @brief Construct an animation data * @param data Buffer containing all keyframe data for this * animation clip * @param tracks Track data @@ -292,11 +307,33 @@ class MAGNUM_TRADE_EXPORT AnimationData { * Each item of @p track should have an @ref Animation::TrackView * instance pointing its key/value views to @p data. The @ref duration() * is automatically calculated from durations of all tracks. + * + * The @ref dataFlags() are implicitly set to a combination of + * @ref DataFlag::Owned and @ref DataFlag::Mutable. For non-owned data + * use the @ref AnimationData(DataFlags, Containers::ArrayView, Containers::Array&&, const void*) + * constructor instead. */ explicit AnimationData(Containers::Array&& data, Containers::Array&& tracks, const void* importerState = nullptr) noexcept; /** - * @brief Construct with explicit duration + * @brief Construct a non-owned animation data + * @param dataFlags Data flags + * @param data View on a buffer containing all keyframe data + * for this animation clip + * @param tracks Track data + * @param importerState Importer-specific state + * @m_since_latest + * + * Compared to @ref AnimationData(Containers::Array&&, Containers::Array&&, const void*) + * creates an instance that doesn't own the passed data. The + * @p dataFlags parameter can contain @ref DataFlag::Mutable to + * indicate the external data can be modified, and is expected to *not* + * have @ref DataFlag::Owned set. + */ + explicit AnimationData(DataFlags dataFlags, Containers::ArrayView data, Containers::Array&& tracks, const void* importerState = nullptr) noexcept; + + /** + * @brief Construct an animation data with explicit duration * @param data Buffer containing all keyframe data for this * animation clip * @param tracks Track data @@ -305,9 +342,32 @@ class MAGNUM_TRADE_EXPORT AnimationData { * * Each item of @p track should have an @ref Animation::TrackView * instance pointing its key/value views to @p data. + * + * The @ref dataFlags() are implicitly set to a combination of + * @ref DataFlag::Owned and @ref DataFlag::Mutable. For non-owned data + * use the @ref AnimationData(DataFlags, Containers::ArrayView, Containers::Array&&, const Range1D&, const void*) + * constructor instead. */ explicit AnimationData(Containers::Array&& data, Containers::Array&& tracks, const Range1D& duration, const void* importerState = nullptr) noexcept; + /** + * @brief Construct a non-owned animation data with explicit duration + * @param dataFlags Data flags + * @param data View on a buffer containing all keyframe data + * for this animation clip + * @param tracks Track data + * @param duration Animation track duration + * @param importerState Importer-specific state + * @m_since_latest + * + * Compared to @ref AnimationData(Containers::Array&&, Containers::Array&&, const Range1D&, const void*) + * creates an instance that doesn't own the passed data. The + * @p dataFlags parameter can contain @ref DataFlag::Mutable to + * indicate the external data can be modified, and is expected to *not* + * have @ref DataFlag::Owned set. + */ + explicit AnimationData(DataFlags dataFlags, Containers::ArrayView data, Containers::Array&& tracks, const Range1D& duration, const void* importerState = nullptr) noexcept; + ~AnimationData(); /** @brief Copying is not allowed */ @@ -322,18 +382,40 @@ class MAGNUM_TRADE_EXPORT AnimationData { /** @brief Move assignment */ AnimationData& operator=(AnimationData&&) noexcept; + /** + * @brief Data flags + * @m_since_latest + * + * @see @ref release(), @ref mutableData(), @ref mutableTrack() + */ + DataFlags dataFlags() const { return _dataFlags; } + /** * @brief Raw data * * Contains data for all tracks contained in this clip. - * @see @ref release() + * @see @ref release(), @ref mutableData() */ - Containers::ArrayView data() & { return _data; } - Containers::ArrayView data() && = delete; /**< @overload */ - - /** @overload */ Containers::ArrayView data() const & { return _data; } - Containers::ArrayView data() const && = delete; /**< @overload */ + + /** @brief Taking a view to a r-value instance is not allowed */ + Containers::ArrayView data() const && = delete; + + /** + * @brief Mutable raw data + * @m_since_latest + * + * Like @ref data(), but returns a non-const view. Expects that the + * animation is mutable. + * @see @ref dataFlags() + */ + Containers::ArrayView mutableData() &; + + /** + * @brief Taking a view to a r-value instance is not allowed + * @m_since_latest + */ + Containers::ArrayView mutableData() && = delete; /** @brief Duration */ Range1D duration() const { return _duration; } @@ -402,6 +484,16 @@ class MAGNUM_TRADE_EXPORT AnimationData { */ const Animation::TrackViewStorage& track(UnsignedInt id) const; + /** + * @brief Mutable track data storage + * @m_since_latest + * + * Like @ref track(), but returns a mutable view. Expects that the + * animation is mutable. + * @see @ref dataFlags() + */ + const Animation::TrackViewStorage& mutableTrack(UnsignedInt id); + /** * @brief Track data * @tparam V Track value type @@ -416,12 +508,24 @@ class MAGNUM_TRADE_EXPORT AnimationData { */ template> const Animation::TrackView& track(UnsignedInt id) const; + /** + * @brief Mutable track data + * @m_since_latest + * + * Like @ref track(), but returns a mutable view. Expects that the + * animation is mutable. + * @see @ref dataFlags() + */ + template> const Animation::TrackView& mutableTrack(UnsignedInt id); + /** * @brief Release data storage * * Releases the ownership of the data array and resets internal state - * to default. - * @see @ref data() + * to default. Note that the returned array has a custom no-op deleter + * when the data are not owned by the animation, and while the returned + * array type is mutable, the actual memory might be not. + * @see @ref data(), @ref dataFlags() */ Containers::Array release() { return std::move(_data); } @@ -438,6 +542,7 @@ class MAGNUM_TRADE_EXPORT AnimationData { implementations. */ friend AbstractImporter; + DataFlags _dataFlags; Range1D _duration; Containers::Array _data; Containers::Array _tracks; @@ -512,6 +617,13 @@ template const Animation::TrackView& return static_cast&>(storage); } +template const Animation::TrackView& AnimationData::mutableTrack(UnsignedInt id) { + const Animation::TrackViewStorage& storage = mutableTrack(id); + CORRADE_ASSERT(Implementation::animationTypeFor() == _tracks[id]._type, "Trade::AnimationData::mutableTrack(): improper type requested for" << _tracks[id]._type, (static_cast&>(storage))); + CORRADE_ASSERT(Implementation::animationTypeFor() == _tracks[id]._resultType, "Trade::AnimationData::mutableTrack(): improper result type requested for" << _tracks[id]._resultType, (static_cast&>(storage))); + return static_cast&>(storage); +} + }} #endif diff --git a/src/Magnum/Trade/Data.h b/src/Magnum/Trade/Data.h index bc74cce2f..b3b04dcfd 100644 --- a/src/Magnum/Trade/Data.h +++ b/src/Magnum/Trade/Data.h @@ -41,8 +41,8 @@ namespace Magnum { namespace Trade { @brief Data flag @m_since_latest -@see @ref DataFlags, @ref MeshData::indexDataFlags(), - @ref MeshData::vertexDataFlags() +@see @ref DataFlags, @ref AnimationData::dataFlags(), + @ref MeshData::indexDataFlags(), @ref MeshData::vertexDataFlags() */ enum class DataFlag: UnsignedByte { /** @@ -71,7 +71,8 @@ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, DataFlag value); @brief Data flags @m_since_latest -@see @ref MeshData::indexDataFlags(), @ref MeshData::vertexDataFlags() +@see @ref AnimationData::dataFlags(), @ref MeshData::indexDataFlags(), + @ref MeshData::vertexDataFlags() */ typedef Containers::EnumSet DataFlags; diff --git a/src/Magnum/Trade/Test/AnimationDataTest.cpp b/src/Magnum/Trade/Test/AnimationDataTest.cpp index 3215a0d17..5830e926d 100644 --- a/src/Magnum/Trade/Test/AnimationDataTest.cpp +++ b/src/Magnum/Trade/Test/AnimationDataTest.cpp @@ -36,15 +36,21 @@ struct AnimationDataTest: TestSuite::Tester { explicit AnimationDataTest(); void construct(); + void constructNotOwned(); void constructImplicitDuration(); void constructImplicitDurationEmpty(); + void constructImplicitDurationNotOwned(); + void constructNotOwnedFlagOwned(); + void constructImplicitDurationNotOwnedFlagOwned(); + void constructCopy(); void constructMove(); void constructTrackDataDefault(); - void trackCustomResultType(); + void mutableAccessNotAllowed(); + void trackCustomResultType(); void trackWrongIndex(); void trackWrongType(); void trackWrongResultType(); @@ -53,17 +59,34 @@ struct AnimationDataTest: TestSuite::Tester { void debugAnimationTrackTargetType(); }; +struct { + const char* name; + DataFlags dataFlags; +} NotOwnedData[] { + {"", {}}, + {"mutable", DataFlag::Mutable}, +}; + AnimationDataTest::AnimationDataTest() { addTests({&AnimationDataTest::construct, &AnimationDataTest::constructImplicitDuration, - &AnimationDataTest::constructImplicitDurationEmpty, + &AnimationDataTest::constructImplicitDurationEmpty}); + + addInstancedTests({&AnimationDataTest::constructNotOwned, + &AnimationDataTest::constructImplicitDurationNotOwned}, + Containers::arraySize(NotOwnedData)); + + addTests({&AnimationDataTest::constructNotOwnedFlagOwned, + &AnimationDataTest::constructImplicitDurationNotOwnedFlagOwned, + &AnimationDataTest::constructCopy, &AnimationDataTest::constructMove, &AnimationDataTest::constructTrackDataDefault, - &AnimationDataTest::trackCustomResultType, + &AnimationDataTest::mutableAccessNotAllowed, + &AnimationDataTest::trackCustomResultType, &AnimationDataTest::trackWrongIndex, &AnimationDataTest::trackWrongType, &AnimationDataTest::trackWrongResultType, @@ -106,8 +129,10 @@ void AnimationDataTest::construct() { animationInterpolatorFor(Animation::Interpolation::Linear)}} }}, {-1.0f, 7.0f}, &state}; + CORRADE_COMPARE(data.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(data.duration(), (Range1D{-1.0f, 7.0f})); - CORRADE_COMPARE(data.data().size(), sizeof(Data)*3); + CORRADE_COMPARE(static_cast(data.data().data()), view.data()); + CORRADE_COMPARE(static_cast(data.mutableData().data()), view.data()); CORRADE_COMPARE(data.trackCount(), 2); CORRADE_COMPARE(data.importerState(), &state); @@ -122,6 +147,12 @@ void AnimationDataTest::construct() { CORRADE_COMPARE(track.values().size(), 3); CORRADE_COMPARE(track.interpolation(), Animation::Interpolation::Constant); CORRADE_COMPARE(track.at(2.5f), (Vector3{3.0f, 1.0f, 0.1f})); + + Animation::TrackView mutableTrack = data.mutableTrack(0); + CORRADE_COMPARE(mutableTrack.keys().size(), 3); + CORRADE_COMPARE(mutableTrack.values().size(), 3); + CORRADE_COMPARE(mutableTrack.interpolation(), Animation::Interpolation::Constant); + CORRADE_COMPARE(mutableTrack.at(2.5f), (Vector3{3.0f, 1.0f, 0.1f})); } { CORRADE_COMPARE(data.trackType(1), AnimationTrackType::Quaternion); CORRADE_COMPARE(data.trackResultType(1), AnimationTrackType::Quaternion); @@ -133,6 +164,8 @@ void AnimationDataTest::construct() { CORRADE_COMPARE(track.values().size(), 3); CORRADE_COMPARE(track.interpolation(), Animation::Interpolation::Linear); CORRADE_COMPARE(track.at(2.5f), Quaternion::rotation(32.5_degf, Vector3::yAxis())); + + /* Testing the mutable track just once is enough */ } } @@ -166,6 +199,7 @@ void AnimationDataTest::constructImplicitDuration() { Animation::Interpolation::Linear}} }}, &state}; + CORRADE_COMPARE(data.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(data.duration(), (Range1D{1.0f, 7.0f})); CORRADE_COMPARE(data.trackCount(), 2); CORRADE_COMPARE(data.importerState(), &state); @@ -181,6 +215,13 @@ void AnimationDataTest::constructImplicitDuration() { CORRADE_COMPARE(track.values().size(), 2); CORRADE_COMPARE(track.interpolation(), Animation::Interpolation::Constant); CORRADE_COMPARE(track.at(6.0f), false); + + Animation::TrackView mutableTrack = data.mutableTrack(0); + CORRADE_COMPARE(mutableTrack.duration(), (Range1D{1.0f, 5.0f})); + CORRADE_COMPARE(mutableTrack.keys().size(), 2); + CORRADE_COMPARE(mutableTrack.values().size(), 2); + CORRADE_COMPARE(mutableTrack.interpolation(), Animation::Interpolation::Constant); + CORRADE_COMPARE(mutableTrack.at(6.0f), false); } { CORRADE_COMPARE(data.trackType(1), AnimationTrackType::Bool); CORRADE_COMPARE(data.trackResultType(1), AnimationTrackType::Bool); @@ -193,6 +234,8 @@ void AnimationDataTest::constructImplicitDuration() { CORRADE_COMPARE(track.values().size(), 2); CORRADE_COMPARE(track.interpolation(), Animation::Interpolation::Linear); CORRADE_COMPARE(track.at(4.5f), true); + + /* Testing the mutable track just once is enough */ } } @@ -201,6 +244,119 @@ void AnimationDataTest::constructImplicitDurationEmpty() { CORRADE_COMPARE(data.duration(), Range1D{}); } +void AnimationDataTest::constructNotOwned() { + auto&& instanceData = NotOwnedData[testCaseInstanceId()]; + setTestCaseDescription(instanceData.name); + + std::pair keyframes[] { + {0.0f, {3.0f, 1.0f, 0.1f}}, + {5.0f, {0.3f, 0.6f, 1.0f}} + }; + + const int state = 5; + AnimationData data{instanceData.dataFlags, keyframes, Containers::Array{Containers::InPlaceInit, { + {AnimationTrackType::Vector3, + AnimationTrackTargetType::Translation3D, 42, + Animation::TrackView{ + keyframes, + Animation::Interpolation::Constant, + animationInterpolatorFor(Animation::Interpolation::Constant)}} + }}, {-1.0f, 7.0f}, &state}; + + CORRADE_COMPARE(data.dataFlags(), instanceData.dataFlags); + CORRADE_COMPARE(data.duration(), (Range1D{-1.0f, 7.0f})); + CORRADE_COMPARE(static_cast(data.data().data()), keyframes); + if(instanceData.dataFlags & DataFlag::Mutable) + CORRADE_COMPARE(static_cast(data.mutableData().data()), keyframes); + CORRADE_COMPARE(data.trackCount(), 1); + CORRADE_COMPARE(data.importerState(), &state); + + { + CORRADE_COMPARE(data.trackType(0), AnimationTrackType::Vector3); + CORRADE_COMPARE(data.trackResultType(0), AnimationTrackType::Vector3); + CORRADE_COMPARE(data.trackTargetType(0), AnimationTrackTargetType::Translation3D); + CORRADE_COMPARE(data.trackTarget(0), 42); + + Animation::TrackView track = data.track(0); + CORRADE_COMPARE(track.keys().size(), 2); + CORRADE_COMPARE(track.values().size(), 2); + CORRADE_COMPARE(track.interpolation(), Animation::Interpolation::Constant); + CORRADE_COMPARE(track.at(2.5f), (Vector3{3.0f, 1.0f, 0.1f})); + + if(instanceData.dataFlags & DataFlag::Mutable) { + Animation::TrackView mutableTrack = data.mutableTrack(0); + CORRADE_COMPARE(mutableTrack.keys().size(), 2); + CORRADE_COMPARE(mutableTrack.values().size(), 2); + CORRADE_COMPARE(mutableTrack.interpolation(), Animation::Interpolation::Constant); + CORRADE_COMPARE(mutableTrack.at(2.5f), (Vector3{3.0f, 1.0f, 0.1f})); + } + } +} + +void AnimationDataTest::constructImplicitDurationNotOwned() { + auto&& instanceData = NotOwnedData[testCaseInstanceId()]; + setTestCaseDescription(instanceData.name); + + std::pair keyframes[] { + {1.0f, true}, + {5.0f, false} + }; + + const int state = 5; + AnimationData data{instanceData.dataFlags, keyframes, Containers::Array{Containers::InPlaceInit, { + {AnimationTrackType::Bool, + AnimationTrackTargetType(129), 0, + Animation::TrackView{keyframes, Animation::Interpolation::Constant}}, + }}, &state}; + + CORRADE_COMPARE(data.dataFlags(), instanceData.dataFlags); + CORRADE_COMPARE(data.duration(), (Range1D{1.0f, 5.0f})); + CORRADE_COMPARE(static_cast(data.data().data()), keyframes); + if(instanceData.dataFlags & DataFlag::Mutable) + CORRADE_COMPARE(static_cast(data.mutableData().data()), keyframes); + CORRADE_COMPARE(data.trackCount(), 1); + CORRADE_COMPARE(data.importerState(), &state); + + { + CORRADE_COMPARE(data.trackType(0), AnimationTrackType::Bool); + CORRADE_COMPARE(data.trackResultType(0), AnimationTrackType::Bool); + CORRADE_COMPARE(data.trackTargetType(0), AnimationTrackTargetType(129)); + CORRADE_COMPARE(data.trackTarget(0), 0); + + Animation::TrackView track = data.track(0); + CORRADE_COMPARE(track.duration(), (Range1D{1.0f, 5.0f})); + CORRADE_COMPARE(track.keys().size(), 2); + CORRADE_COMPARE(track.values().size(), 2); + CORRADE_COMPARE(track.interpolation(), Animation::Interpolation::Constant); + CORRADE_COMPARE(track.at(3.0f), true); + + if(instanceData.dataFlags & DataFlag::Mutable) { + Animation::TrackView mutableTrack = data.mutableTrack(0); + CORRADE_COMPARE(mutableTrack.duration(), (Range1D{1.0f, 5.0f})); + CORRADE_COMPARE(mutableTrack.keys().size(), 2); + CORRADE_COMPARE(mutableTrack.values().size(), 2); + CORRADE_COMPARE(mutableTrack.interpolation(), Animation::Interpolation::Constant); + CORRADE_COMPARE(mutableTrack.at(3.0f), true); + } + } +} + +void AnimationDataTest::constructNotOwnedFlagOwned() { + std::ostringstream out; + Error redirectError{&out}; + AnimationData data{DataFlag::Owned, nullptr, {}, {-1.0f, 7.0f}}; + CORRADE_COMPARE(out.str(), + "Trade::AnimationData: can't construct a non-owned instance with Trade::DataFlag::Owned\n"); +} + +void AnimationDataTest::constructImplicitDurationNotOwnedFlagOwned() { + std::ostringstream out; + Error redirectError{&out}; + AnimationData data{DataFlag::Owned, nullptr, {}}; + CORRADE_COMPARE(out.str(), + "Trade::AnimationData: can't construct a non-owned instance with Trade::DataFlag::Owned\n"); +} + void AnimationDataTest::constructCopy() { CORRADE_VERIFY(!(std::is_constructible{})); CORRADE_VERIFY(!(std::is_assignable{})); @@ -310,6 +466,30 @@ void AnimationDataTest::constructTrackDataDefault() { CORRADE_VERIFY(true); /* no public accessors here, so nothing to check */ } +void AnimationDataTest::mutableAccessNotAllowed() { + const std::pair keyframes[] { + {1.0f, true}, + {5.0f, false} + }; + + AnimationData data{{}, keyframes, Containers::Array{Containers::InPlaceInit, { + {AnimationTrackType::Bool, + AnimationTrackTargetType(129), 0, + Animation::TrackView{keyframes, Animation::Interpolation::Constant}}, + }}}; + CORRADE_COMPARE(data.dataFlags(), DataFlags{}); + + std::ostringstream out; + Error redirectError{&out}; + data.mutableData(); + data.mutableTrack(0); + data.mutableTrack(0); + CORRADE_COMPARE(out.str(), + "Trade::AnimationData::mutableData(): the animation is not mutable\n" + "Trade::AnimationData::mutableTrack(): the animation is not mutable\n" + "Trade::AnimationData::mutableTrack(): the animation is not mutable\n"); +} + void AnimationDataTest::trackCustomResultType() { using namespace Math::Literals;