fix: reset interpolator and initialize it on convert (#11157) 0791ee519d * fix: reset interpolator and initialize it on convert Co-authored-by: hernan <hernan@rive.app>
diff --git a/.rive_head b/.rive_head index c07112e..7c663e5 100644 --- a/.rive_head +++ b/.rive_head
@@ -1 +1 @@ -4f005f715eedd281cc911e739b42109fafee0633 +0791ee519da721cd018cf3955906563ddc576117
diff --git a/include/rive/data_bind/converters/data_converter.hpp b/include/rive/data_bind/converters/data_converter.hpp index 3716221..86e3db8 100644 --- a/include/rive/data_bind/converters/data_converter.hpp +++ b/include/rive/data_bind/converters/data_converter.hpp
@@ -22,7 +22,6 @@ }; virtual DataType outputType() { return DataType::none; }; virtual void bindFromContext(DataContext* dataContext, DataBind* dataBind); - virtual void initialize(DataType inputType) {} virtual void unbind(); StatusCode import(ImportStack& importStack) override; void markConverterDirty(); @@ -30,6 +29,7 @@ void copy(const DataConverter& object); virtual bool advance(float elapsedTime); void addDirtyDataBind(DataBind*) override; + virtual void reset() {}; protected: DataBind* m_parentDataBind;
diff --git a/include/rive/data_bind/converters/data_converter_group.hpp b/include/rive/data_bind/converters/data_converter_group.hpp index 1437c5d..b6829a7 100644 --- a/include/rive/data_bind/converters/data_converter_group.hpp +++ b/include/rive/data_bind/converters/data_converter_group.hpp
@@ -34,10 +34,10 @@ const std::vector<DataConverterGroupItem*>& items() { return m_items; } Core* clone() const override; void bindFromContext(DataContext* dataContext, DataBind* dataBind) override; - void initialize(DataType inputType) override; void unbind() override; void update() override; bool advance(float elapsedSeconds) override; + void reset() override; private: std::vector<DataConverterGroupItem*> m_items;
diff --git a/include/rive/data_bind/converters/data_converter_interpolator.hpp b/include/rive/data_bind/converters/data_converter_interpolator.hpp index 3dadfb6..93027ac 100644 --- a/include/rive/data_bind/converters/data_converter_interpolator.hpp +++ b/include/rive/data_bind/converters/data_converter_interpolator.hpp
@@ -28,6 +28,7 @@ } void dispose() { + elapsedSeconds = 0; if (from != nullptr) { delete from; @@ -44,12 +45,14 @@ template <typename T = DataValue> void initialize(DataConverterInterpolator* converter) { + m_initialized = true; m_converter = converter; m_animationDataA.initialize<T>(); m_animationDataB.initialize<T>(); m_currentValue = new T(); } + bool initialized() { return m_initialized; } void dispose(); void resetValues(DataValue* input) { @@ -58,6 +61,12 @@ input->copyValue(animationData->to); input->copyValue(m_currentValue); } + void reset() + { + dispose(); + m_isSmoothingAnimation = false; + m_initialized = false; + } void updateValues(DataValue* input) { auto animationData = currentAnimationData(); @@ -93,6 +102,7 @@ InterpolatorAnimationData m_animationDataB; InterpolatorAnimationData* currentAnimationData(); bool m_isSmoothingAnimation = false; + bool m_initialized = false; DataValue* m_currentValue = nullptr; DataConverterInterpolator* m_converter = nullptr; }; @@ -110,14 +120,18 @@ DataValue* convert(DataValue* value, DataBind* dataBind) override; DataValue* reverseConvert(DataValue* value, DataBind* dataBind) override; bool advance(float elapsedTime) override; + void reset() override; void copy(const DataConverterInterpolatorBase& object); void durationChanged() override; template <typename T = DataValue> void startAdvancer() { + if (m_output != nullptr) + { + delete m_output; + } m_output = new T(); m_advancer.initialize<T>(this); } - void initialize(DataType inputType) override; private: DataValue* m_output = nullptr;
diff --git a/src/data_bind/converters/data_converter_group.cpp b/src/data_bind/converters/data_converter_group.cpp index d250c77..904093b 100644 --- a/src/data_bind/converters/data_converter_group.cpp +++ b/src/data_bind/converters/data_converter_group.cpp
@@ -74,23 +74,6 @@ } } -void DataConverterGroup::initialize(DataType inputType) -{ - auto currentInputType = inputType; - for (auto& item : m_items) - { - if (item->converter() != nullptr) - { - item->converter()->initialize(currentInputType); - if (item->converter()->outputType() != DataType::input && - item->converter()->outputType() != DataType::none) - { - currentInputType = item->converter()->outputType(); - } - } - } -} - void DataConverterGroup::unbind() { for (auto& item : m_items) @@ -115,6 +98,18 @@ } } +void DataConverterGroup::reset() +{ + for (auto& item : m_items) + { + auto converter = item->converter(); + if (converter != nullptr) + { + converter->reset(); + } + } +} + bool DataConverterGroup::advance(float elapsedSeconds) { bool didUpdate = false;
diff --git a/src/data_bind/converters/data_converter_interpolator.cpp b/src/data_bind/converters/data_converter_interpolator.cpp index bede410..f351f65 100644 --- a/src/data_bind/converters/data_converter_interpolator.cpp +++ b/src/data_bind/converters/data_converter_interpolator.cpp
@@ -92,6 +92,7 @@ } auto prevTime = animationData->elapsedSeconds; advanceAnimationData(elapsedTime); + if (prevTime < m_converter->duration()) { m_converter->markConverterDirty(); @@ -103,21 +104,6 @@ return false; } -void DataConverterInterpolator::initialize(DataType inputType) -{ - switch (inputType) - { - case DataType::number: - startAdvancer<DataValueNumber>(); - break; - case DataType::color: - startAdvancer<DataValueColor>(); - break; - default: - break; - } -} - DataConverterInterpolator::~DataConverterInterpolator() { if (m_output != nullptr) @@ -141,23 +127,38 @@ bool DataConverterInterpolator::advance(float elapsedTime) { - if (m_output == nullptr) - { - return false; - } // Advance can be called multiple times in a single frame. // We want to make sure that two advances with time > 0 have elapsed before // considering the state as second frame. - if (m_advanceCount < 2) + if (m_advanceCount < 2 && elapsedTime > 0) { m_advanceCount++; } + if (!m_advancer.initialized()) + { + return true; + } return m_advancer.advance(elapsedTime); } DataValue* DataConverterInterpolator::convert(DataValue* input, DataBind* dataBind) { + if (!m_advancer.initialized()) + { + if (input->is<DataValueNumber>()) + { + startAdvancer<DataValueNumber>(); + } + else if (input->is<DataValueColor>()) + { + startAdvancer<DataValueColor>(); + } + else + { + return input; + } + } if (m_output != nullptr && (input->is<DataValueNumber>() || input->is<DataValueColor>())) { @@ -175,6 +176,12 @@ return input; } +void DataConverterInterpolator::reset() +{ + m_advanceCount = 0; + m_advancer.reset(); +} + DataValue* DataConverterInterpolator::reverseConvert(DataValue* input, DataBind* dataBind) {
diff --git a/src/data_bind/data_bind.cpp b/src/data_bind/data_bind.cpp index 4c95b3a..4c92711 100644 --- a/src/data_bind/data_bind.cpp +++ b/src/data_bind/data_bind.cpp
@@ -259,6 +259,10 @@ default: break; } + if (m_dataConverter) + { + m_dataConverter->reset(); + } addDirt(ComponentDirt::Bindings, true); }
diff --git a/src/data_bind/data_bind_context.cpp b/src/data_bind/data_bind_context.cpp index bded564..b8867a8 100644 --- a/src/data_bind/data_bind_context.cpp +++ b/src/data_bind/data_bind_context.cpp
@@ -53,7 +53,6 @@ if (m_dataConverter != nullptr) { m_dataConverter->bindFromContext(dataContext, this); - m_dataConverter->initialize(sourceOutputType()); } } }
diff --git a/tests/unit_tests/assets/data_converter_interpolator_reset.riv b/tests/unit_tests/assets/data_converter_interpolator_reset.riv new file mode 100644 index 0000000..35a771b --- /dev/null +++ b/tests/unit_tests/assets/data_converter_interpolator_reset.riv Binary files differ
diff --git a/tests/unit_tests/runtime/data_binding_converters_test.cpp b/tests/unit_tests/runtime/data_binding_converters_test.cpp index 7861040..428791e 100644 --- a/tests/unit_tests/runtime/data_binding_converters_test.cpp +++ b/tests/unit_tests/runtime/data_binding_converters_test.cpp
@@ -3,6 +3,7 @@ #include "rive/animation/linear_animation_instance.hpp" #include "rive/animation/state_machine_instance.hpp" #include "rive/viewmodel/viewmodel.hpp" +#include "rive/viewmodel/viewmodel_instance_color.hpp" #include "rive/viewmodel/viewmodel_instance_number.hpp" #include "rive/viewmodel/viewmodel_instance_trigger.hpp" #include "rive/viewmodel/viewmodel_instance_list.hpp" @@ -59,4 +60,80 @@ } CHECK(silver.matches("list_to_length_test")); +} + +TEST_CASE("data converter interpolator resets on binding", "[silver]") +{ + SerializingFactory silver; + auto file = + ReadRiveFile("assets/data_converter_interpolator_reset.riv", &silver); + + auto artboard = file->artboardDefault(); + + silver.frameSize(artboard->width(), artboard->height()); + + REQUIRE(artboard != nullptr); + auto stateMachine = artboard->stateMachineAt(0); + auto renderer = silver.makeRenderer(); + int viewModelId = artboard.get()->viewModelId(); + { + auto vmi = viewModelId == -1 + ? file->createViewModelInstance(artboard.get()) + : file->createViewModelInstance(viewModelId, 0); + auto numProp = + vmi->propertyValue("xPos")->as<ViewModelInstanceNumber>(); + numProp->propertyValue(250); + auto colProp = vmi->propertyValue("col")->as<ViewModelInstanceColor>(); + auto redColor = (255 << 24) | (255 << 16); + colProp->propertyValue(redColor); + + stateMachine->bindViewModelInstance(vmi); + stateMachine->advanceAndApply(0.1f); + + artboard->draw(renderer.get()); + + auto greenColor = (255 << 24) | (255 << 8); + colProp->propertyValue(greenColor); + numProp->propertyValue(500); + + int frames = (int)(1.0f / 0.016f); + for (int i = 0; i < frames; i++) + { + silver.addFrame(); + stateMachine->advanceAndApply(0.016f); + artboard->draw(renderer.get()); + } + } + // When a new binding is applied, interpolators are reset and the initial + // value is not interpolated + { + silver.addFrame(); + auto vmi = viewModelId == -1 + ? file->createViewModelInstance(artboard.get()) + : file->createViewModelInstance(viewModelId, 0); + auto numProp = + vmi->propertyValue("xPos")->as<ViewModelInstanceNumber>(); + numProp->propertyValue(250); + auto colProp = vmi->propertyValue("col")->as<ViewModelInstanceColor>(); + auto redColor = (255 << 24) | (255 << 16); + colProp->propertyValue(redColor); + stateMachine->bindViewModelInstance(vmi); + stateMachine->advanceAndApply(0.1f); + + artboard->draw(renderer.get()); + + auto blueColor = (255 << 24) | 255; + colProp->propertyValue(blueColor); + numProp->propertyValue(0); + + int frames = (int)(1.0f / 0.016f); + for (int i = 0; i < frames; i++) + { + silver.addFrame(); + stateMachine->advanceAndApply(0.016f); + artboard->draw(renderer.get()); + } + } + + CHECK(silver.matches("data_converter_interpolator_reset")); } \ No newline at end of file
diff --git a/tests/unit_tests/silvers/data_converter_interpolator_reset.sriv b/tests/unit_tests/silvers/data_converter_interpolator_reset.sriv new file mode 100644 index 0000000..1f8092d --- /dev/null +++ b/tests/unit_tests/silvers/data_converter_interpolator_reset.sriv Binary files differ