animation: use smart pointers

This commit is contained in:
Maximilian Seidler 2024-12-28 09:29:06 +01:00
parent 4984b5ab12
commit 2e21448de5
5 changed files with 146 additions and 90 deletions

View file

@ -1,5 +1,8 @@
#pragma once #pragma once
#include "../memory/WeakPtr.hpp"
#include "hyprutils/memory/SharedPtr.hpp"
#include <functional> #include <functional>
#include <chrono> #include <chrono>
@ -19,20 +22,20 @@ namespace Hyprutils {
float internalSpeed = 0.f; float internalSpeed = 0.f;
int internalEnabled = -1; int internalEnabled = -1;
SAnimationPropertyConfig* pValues = nullptr; Memory::CWeakPointer<SAnimationPropertyConfig> pValues;
SAnimationPropertyConfig* pParentAnimation = nullptr; Memory::CWeakPointer<SAnimationPropertyConfig> pParentAnimation;
}; };
/* A base class for animated variables. */ /* A base class for animated variables. */
class CBaseAnimatedVariable { class CBaseAnimatedVariable {
public: public:
using CallbackFun = std::function<void(CBaseAnimatedVariable* thisptr)>; using CallbackFun = std::function<void(Memory::CWeakPointer<CBaseAnimatedVariable> thisptr)>;
CBaseAnimatedVariable() { CBaseAnimatedVariable() {
; // m_bDummy = true; ; // m_bDummy = true;
}; };
void create(CAnimationManager* p, int typeInfo); void create(CAnimationManager*, int, Memory::CSharedPointer<CBaseAnimatedVariable>);
void connectToActive(); void connectToActive();
void disconnectFromActive(); void disconnectFromActive();
@ -48,11 +51,11 @@ namespace Hyprutils {
CBaseAnimatedVariable& operator=(const CBaseAnimatedVariable&) = delete; CBaseAnimatedVariable& operator=(const CBaseAnimatedVariable&) = delete;
CBaseAnimatedVariable& operator=(CBaseAnimatedVariable&&) = delete; CBaseAnimatedVariable& operator=(CBaseAnimatedVariable&&) = delete;
void setConfig(SAnimationPropertyConfig* pConfig) { void setConfig(Memory::CSharedPointer<SAnimationPropertyConfig> pConfig) {
m_pConfig = pConfig; m_pConfig = pConfig;
} }
SAnimationPropertyConfig* getConfig() const { Memory::CWeakPointer<SAnimationPropertyConfig> getConfig() const {
return m_pConfig; return m_pConfig;
} }
@ -103,8 +106,10 @@ namespace Hyprutils {
bool m_bIsConnectedToActive = false; bool m_bIsConnectedToActive = false;
bool m_bIsBeingAnimated = false; bool m_bIsBeingAnimated = false;
Memory::CWeakPointer<CBaseAnimatedVariable> m_pSelf;
private: private:
SAnimationPropertyConfig* m_pConfig; Memory::CWeakPointer<SAnimationPropertyConfig> m_pConfig;
std::chrono::steady_clock::time_point animationBegin; std::chrono::steady_clock::time_point animationBegin;
@ -138,12 +143,13 @@ namespace Hyprutils {
public: public:
CGenericAnimatedVariable() = default; CGenericAnimatedVariable() = default;
void create(const int typeInfo, const VarType& initialValue, CAnimationManager* pAnimationManager) { void create(const int typeInfo, CAnimationManager* pAnimationManager, Memory::CSharedPointer<CGenericAnimatedVariable<VarType, AnimationContext>> pSelf,
const VarType& initialValue) {
m_Begun = initialValue; m_Begun = initialValue;
m_Value = initialValue; m_Value = initialValue;
m_Goal = initialValue; m_Goal = initialValue;
CBaseAnimatedVariable::create(pAnimationManager, typeInfo); CBaseAnimatedVariable::create(pAnimationManager, typeInfo, pSelf);
} }
CGenericAnimatedVariable(const CGenericAnimatedVariable&) = delete; CGenericAnimatedVariable(const CGenericAnimatedVariable&) = delete;

View file

@ -3,7 +3,7 @@
#include "./BezierCurve.hpp" #include "./BezierCurve.hpp"
#include "./AnimatedVariable.hpp" #include "./AnimatedVariable.hpp"
#include "../math/Vector2D.hpp" #include "../math/Vector2D.hpp"
#include "../memory/SharedPtr.hpp" #include "../memory/WeakPtr.hpp"
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
@ -13,7 +13,6 @@ using namespace Hyprutils::Math;
namespace Hyprutils { namespace Hyprutils {
namespace Animation { namespace Animation {
/* A class for managing bezier curves and variables that are being animated. */ /* A class for managing bezier curves and variables that are being animated. */
class CAnimationManager { class CAnimationManager {
public: public:
@ -33,7 +32,7 @@ namespace Hyprutils {
std::unordered_map<std::string, CSharedPointer<CBezierCurve>> getAllBeziers(); std::unordered_map<std::string, CSharedPointer<CBezierCurve>> getAllBeziers();
std::vector<CBaseAnimatedVariable*> m_vActiveAnimatedVariables; std::vector<CWeakPointer<CBaseAnimatedVariable>> m_vActiveAnimatedVariables;
private: private:
std::unordered_map<std::string, CSharedPointer<CBezierCurve>> m_mBezierCurves; std::unordered_map<std::string, CSharedPointer<CBezierCurve>> m_mBezierCurves;

View file

@ -1,11 +1,17 @@
#include <hyprutils/animation/AnimatedVariable.hpp> #include <hyprutils/animation/AnimatedVariable.hpp>
#include <hyprutils/animation/AnimationManager.hpp> #include <hyprutils/animation/AnimationManager.hpp>
#include <hyprutils/memory/WeakPtr.hpp>
using namespace Hyprutils::Animation; using namespace Hyprutils::Animation;
using namespace Hyprutils::Memory;
void CBaseAnimatedVariable::create(Hyprutils::Animation::CAnimationManager* pAnimationManager, int typeInfo) { #define SP CSharedPointer
#define WP CWeakPointer
void CBaseAnimatedVariable::create(Hyprutils::Animation::CAnimationManager* pAnimationManager, int typeInfo, SP<CBaseAnimatedVariable> pSelf) {
m_pAnimationManager = pAnimationManager; m_pAnimationManager = pAnimationManager;
m_Type = typeInfo; m_Type = typeInfo;
m_pSelf = pSelf;
m_bDummy = false; m_bDummy = false;
} }
@ -16,7 +22,7 @@ void CBaseAnimatedVariable::connectToActive() {
m_pAnimationManager->scheduleTick(); // otherwise the animation manager will never pick this up m_pAnimationManager->scheduleTick(); // otherwise the animation manager will never pick this up
if (!m_bIsConnectedToActive) if (!m_bIsConnectedToActive)
m_pAnimationManager->m_vActiveAnimatedVariables.push_back(this); m_pAnimationManager->m_vActiveAnimatedVariables.push_back(m_pSelf);
m_bIsConnectedToActive = true; m_bIsConnectedToActive = true;
} }
@ -24,22 +30,39 @@ void CBaseAnimatedVariable::disconnectFromActive() {
if (!m_pAnimationManager) if (!m_pAnimationManager)
return; return;
std::erase_if(m_pAnimationManager->m_vActiveAnimatedVariables, [&](const auto& other) { return other == this; }); std::erase_if(m_pAnimationManager->m_vActiveAnimatedVariables, [&](const auto& other) { return other == m_pSelf; });
m_bIsConnectedToActive = false; m_bIsConnectedToActive = false;
} }
bool Hyprutils::Animation::CBaseAnimatedVariable::enabled() const { bool Hyprutils::Animation::CBaseAnimatedVariable::enabled() const {
return m_pConfig ? m_pConfig->pValues->internalEnabled : false; if (const auto PCONFIG = m_pConfig.lock()) {
const auto PVALUES = PCONFIG->pValues.lock();
return PVALUES ? PVALUES->internalEnabled : false;
}
return false;
} }
const std::string& CBaseAnimatedVariable::getBezierName() const { const std::string& CBaseAnimatedVariable::getBezierName() const {
static constexpr const std::string DEFAULTBEZIERNAME = "default"; static constexpr const std::string DEFAULTBEZIERNAME = "default";
return m_pConfig ? m_pConfig->pValues->internalBezier : DEFAULTBEZIERNAME;
if (const auto PCONFIG = m_pConfig.lock()) {
const auto PVALUES = PCONFIG->pValues.lock();
return PVALUES ? PVALUES->internalBezier : DEFAULTBEZIERNAME;
}
return DEFAULTBEZIERNAME;
} }
const std::string& CBaseAnimatedVariable::getStyle() const { const std::string& CBaseAnimatedVariable::getStyle() const {
static constexpr const std::string DEFAULTSTYLE = ""; static constexpr const std::string DEFAULTSTYLE = "";
return m_pConfig ? m_pConfig->pValues->internalStyle : DEFAULTSTYLE;
if (const auto PCONFIG = m_pConfig.lock()) {
const auto PVALUES = PCONFIG->pValues.lock();
return PVALUES ? PVALUES->internalStyle : DEFAULTSTYLE;
}
return DEFAULTSTYLE;
} }
float CBaseAnimatedVariable::getPercent() const { float CBaseAnimatedVariable::getPercent() const {
@ -51,12 +74,22 @@ float CBaseAnimatedVariable::getCurveValue() const {
if (!m_bIsBeingAnimated || !m_pAnimationManager) if (!m_bIsBeingAnimated || !m_pAnimationManager)
return 1.f; return 1.f;
const auto SPENT = getPercent(); std::string bezierName = "";
if (const auto PCONFIG = m_pConfig.lock()) {
const auto PVALUES = PCONFIG->pValues.lock();
if (PVALUES)
bezierName = PVALUES->internalBezier;
}
const auto BEZIER = m_pAnimationManager->getBezier(bezierName);
if (!BEZIER)
return 1.f;
const auto SPENT = getPercent();
if (SPENT >= 1.f) if (SPENT >= 1.f)
return 1.f; return 1.f;
return m_pAnimationManager->getBezier(m_pConfig->pValues->internalBezier)->getYForPoint(SPENT); return BEZIER->getYForPoint(SPENT);
} }
bool CBaseAnimatedVariable::ok() const { bool CBaseAnimatedVariable::ok() const {
@ -65,7 +98,7 @@ bool CBaseAnimatedVariable::ok() const {
void CBaseAnimatedVariable::onUpdate() { void CBaseAnimatedVariable::onUpdate() {
if (m_fUpdateCallback) if (m_fUpdateCallback)
m_fUpdateCallback(this); m_fUpdateCallback(m_pSelf);
} }
void CBaseAnimatedVariable::setCallbackOnEnd(CallbackFun func, bool remove) { void CBaseAnimatedVariable::setCallbackOnEnd(CallbackFun func, bool remove) {
@ -100,7 +133,7 @@ void CBaseAnimatedVariable::onAnimationEnd() {
if (m_fEndCallback) { if (m_fEndCallback) {
/* loading m_bRemoveEndAfterRan before calling the callback allows the callback to delete this animation safely if it is false. */ /* loading m_bRemoveEndAfterRan before calling the callback allows the callback to delete this animation safely if it is false. */
auto removeEndCallback = m_bRemoveEndAfterRan; auto removeEndCallback = m_bRemoveEndAfterRan;
m_fEndCallback(this); m_fEndCallback(m_pSelf);
if (removeEndCallback) if (removeEndCallback)
m_fEndCallback = nullptr; // reset m_fEndCallback = nullptr; // reset
} }
@ -112,7 +145,7 @@ void CBaseAnimatedVariable::onAnimationBegin() {
connectToActive(); connectToActive();
if (m_fBeginCallback) { if (m_fBeginCallback) {
m_fBeginCallback(this); m_fBeginCallback(m_pSelf);
if (m_bRemoveBeginAfterRan) if (m_bRemoveBeginAfterRan)
m_fBeginCallback = nullptr; // reset m_fBeginCallback = nullptr; // reset
} }

View file

@ -37,14 +37,18 @@ bool CAnimationManager::shouldTickForNext() {
} }
void CAnimationManager::tickDone() { void CAnimationManager::tickDone() {
std::vector<CBaseAnimatedVariable*> active; std::vector<CWeakPointer<CBaseAnimatedVariable>> active;
// avoid reallocations // avoid reallocations
active.reserve(m_vActiveAnimatedVariables.size()); active.reserve(m_vActiveAnimatedVariables.size());
for (auto const& av : m_vActiveAnimatedVariables) { for (auto const& av : m_vActiveAnimatedVariables) {
if (av->ok() && av->isBeingAnimated()) const auto PAV = av.lock();
if (!PAV)
continue;
if (PAV->ok() && PAV->isBeingAnimated())
active.push_back(av); active.push_back(av);
else else
av->m_bIsConnectedToActive = false; PAV->m_bIsConnectedToActive = false;
} }
m_vActiveAnimatedVariables = std::move(active); m_vActiveAnimatedVariables = std::move(active);

View file

@ -1,15 +1,26 @@
#include <hyprutils/animation/AnimationManager.hpp> #include <hyprutils/animation/AnimationManager.hpp>
#include <hyprutils/animation/AnimatedVariable.hpp> #include <hyprutils/animation/AnimatedVariable.hpp>
#include <hyprutils/memory/WeakPtr.hpp>
#include "shared.hpp" #include "shared.hpp"
#define SP CSharedPointer
#define WP CWeakPointer
using namespace Hyprutils::Animation; using namespace Hyprutils::Animation;
using namespace Hyprutils::Math; using namespace Hyprutils::Math;
using namespace Hyprutils::Memory;
class EmtpyContext {}; class EmtpyContext {};
template <typename VarType> template <typename VarType>
using CAnimatedVariable = CGenericAnimatedVariable<VarType, EmtpyContext>; using CAnimatedVariable = CGenericAnimatedVariable<VarType, EmtpyContext>;
template <typename VarType>
using PANIMVAR = SP<CAnimatedVariable<VarType>>;
template <typename VarType>
using PANIMVARREF = WP<CAnimatedVariable<VarType>>;
enum eAVTypes { enum eAVTypes {
INT = 1, INT = 1,
TEST, TEST,
@ -26,30 +37,31 @@ struct SomeTestType {
} }
}; };
#define INITANIMCFG(name) animationConfig[name] = {} #define INITANIMCFG(name) animationConfig[name] = makeShared<SAnimationPropertyConfig>();
#define CREATEANIMCFG(name, parent) animationConfig[name] = {false, "", "", 0.f, -1, &animationConfig["global"], &animationConfig[parent]} #define CREATEANIMCFG(name, parent) *animationConfig[name] = {false, "", "", 0.f, -1, animationConfig["global"], animationConfig[parent]}
std::unordered_map<std::string, SAnimationPropertyConfig> animationConfig; std::unordered_map<std::string, SP<SAnimationPropertyConfig>> animationConfig;
class CMyAnimationManager : public CAnimationManager { class CMyAnimationManager : public CAnimationManager {
public: public:
void tick() { void tick() {
for (auto const& av : m_vActiveAnimatedVariables) { for (auto const& av : m_vActiveAnimatedVariables) {
if (!av->ok()) const auto PAV = av.lock();
if (!PAV || !PAV->ok())
continue; continue;
const auto SPENT = av->getPercent(); const auto SPENT = PAV->getPercent();
const auto PBEZIER = getBezier(av->getBezierName()); const auto PBEZIER = getBezier(PAV->getBezierName());
const auto POINTY = PBEZIER->getYForPoint(SPENT); const auto POINTY = PBEZIER->getYForPoint(SPENT);
if (POINTY >= 1.f || !av->enabled()) { if (POINTY >= 1.f || !PAV->enabled()) {
av->warp(); PAV->warp();
continue; continue;
} }
switch (av->m_Type) { switch (PAV->m_Type) {
case eAVTypes::INT: { case eAVTypes::INT: {
auto* avInt = dynamic_cast<CAnimatedVariable<int>*>(av); auto avInt = dynamic_cast<CAnimatedVariable<int>*>(PAV.get());
if (!avInt) if (!avInt)
std::cout << Colors::RED << "Dynamic cast upcast failed" << Colors::RESET; std::cout << Colors::RED << "Dynamic cast upcast failed" << Colors::RESET;
@ -57,7 +69,7 @@ class CMyAnimationManager : public CAnimationManager {
avInt->value() = avInt->begun() + (DELTA * POINTY); avInt->value() = avInt->begun() + (DELTA * POINTY);
} break; } break;
case eAVTypes::TEST: { case eAVTypes::TEST: {
auto* avCustom = dynamic_cast<CAnimatedVariable<SomeTestType>*>(av); auto avCustom = dynamic_cast<CAnimatedVariable<SomeTestType>*>(PAV.get());
if (!avCustom) if (!avCustom)
std::cout << Colors::RED << "Dynamic cast upcast failed" << Colors::RESET; std::cout << Colors::RED << "Dynamic cast upcast failed" << Colors::RESET;
@ -75,11 +87,14 @@ class CMyAnimationManager : public CAnimationManager {
tickDone(); tickDone();
} }
template <typename AnimType> template <typename VarType>
void addAnimation(const AnimType& v, CAnimatedVariable<AnimType>& av, const std::string& animationConfigName) { void createAnimation(const VarType& v, PANIMVAR<VarType>& av, const std::string& animationConfigName) {
constexpr const eAVTypes EAVTYPE = std::is_same_v<AnimType, int> ? eAVTypes::INT : eAVTypes::TEST; constexpr const eAVTypes EAVTYPE = std::is_same_v<VarType, int> ? eAVTypes::INT : eAVTypes::TEST;
av.create(EAVTYPE, v, static_cast<CAnimationManager*>(this)); const auto PAV = makeShared<CGenericAnimatedVariable<VarType, EmtpyContext>>();
av.setConfig(&animationConfig[animationConfigName]);
PAV->create(EAVTYPE, static_cast<CAnimationManager*>(this), PAV, v);
PAV->setConfig(animationConfig[animationConfigName]);
av = std::move(PAV);
} }
virtual void scheduleTick() { virtual void scheduleTick() {
@ -96,64 +111,63 @@ CMyAnimationManager gAnimationManager;
class Subject { class Subject {
public: public:
Subject(const int& a, const int& b) { Subject(const int& a, const int& b) {
gAnimationManager.addAnimation(a, m_iA, "default"); gAnimationManager.createAnimation(a, m_iA, "default");
gAnimationManager.addAnimation(b, m_iB, "default"); gAnimationManager.createAnimation(b, m_iB, "default");
gAnimationManager.addAnimation({}, m_iC, "default"); gAnimationManager.createAnimation({}, m_iC, "default");
} }
CAnimatedVariable<int> m_iA; PANIMVAR<int> m_iA;
CAnimatedVariable<int> m_iB; PANIMVAR<int> m_iB;
CAnimatedVariable<SomeTestType> m_iC; PANIMVAR<SomeTestType> m_iC;
}; };
int main(int argc, char** argv, char** envp) { int main(int argc, char** argv, char** envp) {
INITANIMCFG("global"); animationConfig["default"] = makeShared<SAnimationPropertyConfig>();
CREATEANIMCFG("default", "global"); animationConfig["default"]->internalBezier = "default";
animationConfig["default"].internalBezier = "default"; animationConfig["default"]->internalSpeed = 1.0;
animationConfig["default"].internalSpeed = 1.0; animationConfig["default"]->internalEnabled = 1;
animationConfig["default"].internalEnabled = 1; animationConfig["default"]->pValues = animationConfig["default"];
animationConfig["default"].pValues = &animationConfig["default"];
int ret = 0; int ret = 0;
Subject s(0, 0); Subject s(0, 0);
EXPECT(s.m_iA.value(), 0); EXPECT(s.m_iA->value(), 0);
EXPECT(s.m_iB.value(), 0); EXPECT(s.m_iB->value(), 0);
// Test destruction of a CAnimatedVariable // Test destruction of a CAnimatedVariable
{ {
Subject s2(10, 10); Subject s2(10, 10);
// Adds them to active // Adds them to active
s2.m_iA = 1; *s2.m_iA = 1;
s2.m_iB = 2; *s2.m_iB = 2;
// We deliberately do not tick here, to make sure the destructor removes active animated variables // We deliberately do not tick here, to make sure the destructor removes active animated variables
} }
EXPECT(gAnimationManager.shouldTickForNext(), false); EXPECT(gAnimationManager.shouldTickForNext(), false);
EXPECT(s.m_iC.value().done, false); EXPECT(s.m_iC->value().done, false);
s.m_iA = 10; *s.m_iA = 10;
s.m_iB = 100; *s.m_iB = 100;
s.m_iC = SomeTestType(true); *s.m_iC = SomeTestType(true);
EXPECT(s.m_iC.value().done, false); EXPECT(s.m_iC->value().done, false);
while (gAnimationManager.shouldTickForNext()) { while (gAnimationManager.shouldTickForNext()) {
gAnimationManager.tick(); gAnimationManager.tick();
} }
EXPECT(s.m_iA.value(), 10); EXPECT(s.m_iA->value(), 10);
EXPECT(s.m_iB.value(), 100); EXPECT(s.m_iB->value(), 100);
EXPECT(s.m_iC.value().done, true); EXPECT(s.m_iC->value().done, true);
s.m_iA.setValue(0); s.m_iA->setValue(0);
s.m_iB.setValue(0); s.m_iB->setValue(0);
while (gAnimationManager.shouldTickForNext()) { while (gAnimationManager.shouldTickForNext()) {
gAnimationManager.tick(); gAnimationManager.tick();
} }
EXPECT(s.m_iA.value(), 10); EXPECT(s.m_iA->value(), 10);
EXPECT(s.m_iB.value(), 100); EXPECT(s.m_iB->value(), 100);
// //
// Test callbacks // Test callbacks
@ -161,11 +175,11 @@ int main(int argc, char** argv, char** envp) {
bool beginCallbackRan = false; bool beginCallbackRan = false;
bool updateCallbackRan = false; bool updateCallbackRan = false;
bool endCallbackRan = false; bool endCallbackRan = false;
s.m_iA.setCallbackOnBegin([&beginCallbackRan](CBaseAnimatedVariable* av) { beginCallbackRan = true; }); s.m_iA->setCallbackOnBegin([&beginCallbackRan](WP<CBaseAnimatedVariable> pav) { beginCallbackRan = true; });
s.m_iA.setUpdateCallback([&updateCallbackRan](CBaseAnimatedVariable* av) { updateCallbackRan = true; }); s.m_iA->setUpdateCallback([&updateCallbackRan](WP<CBaseAnimatedVariable> pav) { updateCallbackRan = true; });
s.m_iA.setCallbackOnEnd([&endCallbackRan](CBaseAnimatedVariable* av) { endCallbackRan = true; }, false); s.m_iA->setCallbackOnEnd([&endCallbackRan](WP<CBaseAnimatedVariable> pav) { endCallbackRan = true; }, false);
s.m_iA.setValueAndWarp(42); s.m_iA->setValueAndWarp(42);
EXPECT(beginCallbackRan, false); EXPECT(beginCallbackRan, false);
EXPECT(updateCallbackRan, true); EXPECT(updateCallbackRan, true);
@ -175,7 +189,7 @@ int main(int argc, char** argv, char** envp) {
updateCallbackRan = false; updateCallbackRan = false;
endCallbackRan = false; endCallbackRan = false;
s.m_iA = 1337; *s.m_iA = 1337;
while (gAnimationManager.shouldTickForNext()) { while (gAnimationManager.shouldTickForNext()) {
gAnimationManager.tick(); gAnimationManager.tick();
} }