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
#include "../memory/WeakPtr.hpp"
#include "hyprutils/memory/SharedPtr.hpp"
#include <functional>
#include <chrono>
@ -12,27 +15,27 @@ namespace Hyprutils {
Config properties need to have a static lifetime to allow for config reload.
*/
struct SAnimationPropertyConfig {
bool overridden = true;
bool overridden = true;
std::string internalBezier = "";
std::string internalStyle = "";
float internalSpeed = 0.f;
int internalEnabled = -1;
std::string internalBezier = "";
std::string internalStyle = "";
float internalSpeed = 0.f;
int internalEnabled = -1;
SAnimationPropertyConfig* pValues = nullptr;
SAnimationPropertyConfig* pParentAnimation = nullptr;
Memory::CWeakPointer<SAnimationPropertyConfig> pValues;
Memory::CWeakPointer<SAnimationPropertyConfig> pParentAnimation;
};
/* A base class for animated variables. */
class CBaseAnimatedVariable {
public:
using CallbackFun = std::function<void(CBaseAnimatedVariable* thisptr)>;
using CallbackFun = std::function<void(Memory::CWeakPointer<CBaseAnimatedVariable> thisptr)>;
CBaseAnimatedVariable() {
; // m_bDummy = true;
};
void create(CAnimationManager* p, int typeInfo);
void create(CAnimationManager*, int, Memory::CSharedPointer<CBaseAnimatedVariable>);
void connectToActive();
void disconnectFromActive();
@ -48,11 +51,11 @@ namespace Hyprutils {
CBaseAnimatedVariable& operator=(const CBaseAnimatedVariable&) = delete;
CBaseAnimatedVariable& operator=(CBaseAnimatedVariable&&) = delete;
void setConfig(SAnimationPropertyConfig* pConfig) {
void setConfig(Memory::CSharedPointer<SAnimationPropertyConfig> pConfig) {
m_pConfig = pConfig;
}
SAnimationPropertyConfig* getConfig() const {
Memory::CWeakPointer<SAnimationPropertyConfig> getConfig() const {
return m_pConfig;
}
@ -100,23 +103,25 @@ namespace Hyprutils {
protected:
friend class CAnimationManager;
bool m_bIsConnectedToActive = false;
bool m_bIsBeingAnimated = false;
bool m_bIsConnectedToActive = false;
bool m_bIsBeingAnimated = false;
Memory::CWeakPointer<CBaseAnimatedVariable> m_pSelf;
private:
SAnimationPropertyConfig* m_pConfig;
Memory::CWeakPointer<SAnimationPropertyConfig> m_pConfig;
std::chrono::steady_clock::time_point animationBegin;
std::chrono::steady_clock::time_point animationBegin;
bool m_bDummy = true;
bool m_bDummy = true;
CAnimationManager* m_pAnimationManager;
bool m_bRemoveEndAfterRan = true;
bool m_bRemoveBeginAfterRan = true;
CAnimationManager* m_pAnimationManager;
bool m_bRemoveEndAfterRan = true;
bool m_bRemoveBeginAfterRan = true;
CallbackFun m_fEndCallback;
CallbackFun m_fBeginCallback;
CallbackFun m_fUpdateCallback;
CallbackFun m_fEndCallback;
CallbackFun m_fBeginCallback;
CallbackFun m_fUpdateCallback;
};
/* This concept represents the minimum requirement for a type to be used with CGenericAnimatedVariable */
@ -138,12 +143,13 @@ namespace Hyprutils {
public:
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_Value = initialValue;
m_Goal = initialValue;
CBaseAnimatedVariable::create(pAnimationManager, typeInfo);
CBaseAnimatedVariable::create(pAnimationManager, typeInfo, pSelf);
}
CGenericAnimatedVariable(const CGenericAnimatedVariable&) = delete;

View file

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

View file

@ -1,11 +1,17 @@
#include <hyprutils/animation/AnimatedVariable.hpp>
#include <hyprutils/animation/AnimationManager.hpp>
#include <hyprutils/memory/WeakPtr.hpp>
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_Type = typeInfo;
m_pSelf = pSelf;
m_bDummy = false;
}
@ -16,7 +22,7 @@ void CBaseAnimatedVariable::connectToActive() {
m_pAnimationManager->scheduleTick(); // otherwise the animation manager will never pick this up
if (!m_bIsConnectedToActive)
m_pAnimationManager->m_vActiveAnimatedVariables.push_back(this);
m_pAnimationManager->m_vActiveAnimatedVariables.push_back(m_pSelf);
m_bIsConnectedToActive = true;
}
@ -24,22 +30,39 @@ void CBaseAnimatedVariable::disconnectFromActive() {
if (!m_pAnimationManager)
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;
}
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 {
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 {
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 {
@ -51,12 +74,22 @@ float CBaseAnimatedVariable::getCurveValue() const {
if (!m_bIsBeingAnimated || !m_pAnimationManager)
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)
return 1.f;
return m_pAnimationManager->getBezier(m_pConfig->pValues->internalBezier)->getYForPoint(SPENT);
return BEZIER->getYForPoint(SPENT);
}
bool CBaseAnimatedVariable::ok() const {
@ -65,7 +98,7 @@ bool CBaseAnimatedVariable::ok() const {
void CBaseAnimatedVariable::onUpdate() {
if (m_fUpdateCallback)
m_fUpdateCallback(this);
m_fUpdateCallback(m_pSelf);
}
void CBaseAnimatedVariable::setCallbackOnEnd(CallbackFun func, bool remove) {
@ -100,7 +133,7 @@ void CBaseAnimatedVariable::onAnimationEnd() {
if (m_fEndCallback) {
/* loading m_bRemoveEndAfterRan before calling the callback allows the callback to delete this animation safely if it is false. */
auto removeEndCallback = m_bRemoveEndAfterRan;
m_fEndCallback(this);
m_fEndCallback(m_pSelf);
if (removeEndCallback)
m_fEndCallback = nullptr; // reset
}
@ -112,7 +145,7 @@ void CBaseAnimatedVariable::onAnimationBegin() {
connectToActive();
if (m_fBeginCallback) {
m_fBeginCallback(this);
m_fBeginCallback(m_pSelf);
if (m_bRemoveBeginAfterRan)
m_fBeginCallback = nullptr; // reset
}

View file

@ -37,14 +37,18 @@ bool CAnimationManager::shouldTickForNext() {
}
void CAnimationManager::tickDone() {
std::vector<CBaseAnimatedVariable*> active;
std::vector<CWeakPointer<CBaseAnimatedVariable>> active;
// avoid reallocations
active.reserve(m_vActiveAnimatedVariables.size());
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);
else
av->m_bIsConnectedToActive = false;
PAV->m_bIsConnectedToActive = false;
}
m_vActiveAnimatedVariables = std::move(active);

View file

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