Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

animation: add BezierCurve, AnimationManager and AnimatedVariable #27

Merged
merged 13 commits into from
Dec 29, 2024
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ add_test(
COMMAND hyprutils_filedescriptor "filedescriptor")
add_dependencies(tests hyprutils_filedescriptor)

add_executable(hyprutils_animation "tests/animation.cpp")
target_link_libraries(hyprutils_animation PRIVATE hyprutils PkgConfig::deps)
add_test(
NAME "Animation"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
COMMAND hyprutils_animation "utils")
add_dependencies(tests hyprutils_animation)

# Installation
install(TARGETS hyprutils)
install(DIRECTORY "include/hyprutils" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
Expand Down
230 changes: 230 additions & 0 deletions include/hyprutils/animation/AnimatedVariable.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
#pragma once

#include "../memory/WeakPtr.hpp"
#include "hyprutils/memory/SharedPtr.hpp"

#include <functional>
#include <chrono>

namespace Hyprutils {
namespace Animation {
class CAnimationManager;

/*
Structure for animation properties.
Config properties need to have a static lifetime to allow for config reload.
*/
struct SAnimationPropertyConfig {
bool overridden = true;

std::string internalBezier = "";
std::string internalStyle = "";
float internalSpeed = 0.f;
int internalEnabled = -1;

Memory::CWeakPointer<SAnimationPropertyConfig> pValues;
Memory::CWeakPointer<SAnimationPropertyConfig> pParentAnimation;
};

/* A base class for animated variables. */
class CBaseAnimatedVariable {
public:
using CallbackFun = std::function<void(Memory::CWeakPointer<CBaseAnimatedVariable> thisptr)>;

CBaseAnimatedVariable() {
; // m_bDummy = true;
};

void create(CAnimationManager*, int, Memory::CSharedPointer<CBaseAnimatedVariable>);
void connectToActive();
void disconnectFromActive();

/* Needs to call disconnectFromActive to remove `m_pSelf` from the active animation list */
virtual ~CBaseAnimatedVariable() {
disconnectFromActive();
};

virtual void warp(bool endCallback = true) = 0;

CBaseAnimatedVariable(const CBaseAnimatedVariable&) = delete;
CBaseAnimatedVariable(CBaseAnimatedVariable&&) = delete;
CBaseAnimatedVariable& operator=(const CBaseAnimatedVariable&) = delete;
CBaseAnimatedVariable& operator=(CBaseAnimatedVariable&&) = delete;

void setConfig(Memory::CSharedPointer<SAnimationPropertyConfig> pConfig) {
m_pConfig = pConfig;
}

Memory::CWeakPointer<SAnimationPropertyConfig> getConfig() const {
return m_pConfig;
}

bool enabled() const;
const std::string& getBezierName() const;
const std::string& getStyle() const;

/* returns the spent (completion) % */
float getPercent() const;

/* returns the current curve value */
float getCurveValue() const;

/* checks if an animation is in progress */
bool isBeingAnimated() const {
return m_bIsBeingAnimated;
}

/* checks m_bDummy and m_pAnimationManager */
bool ok() const;

/* calls the update callback */
void onUpdate();

/* sets a function to be ran when an animation ended.
if "remove" is set to true, it will remove the callback when ran. */
void setCallbackOnEnd(CallbackFun func, bool remove = true);

/* sets a function to be ran when an animation is started.
if "remove" is set to true, it will remove the callback when ran. */
void setCallbackOnBegin(CallbackFun func, bool remove = true);

/* sets the update callback, called every time the value is animated and a step is done
Warning: calling unregisterVar/registerVar in this handler will cause UB */
void setUpdateCallback(CallbackFun func);

/* resets all callbacks. Does not call any. */
void resetAllCallbacks();

void onAnimationEnd();
void onAnimationBegin();

int m_Type = -1;

protected:
friend class CAnimationManager;

bool m_bIsConnectedToActive = false;
bool m_bIsBeingAnimated = false;

Memory::CWeakPointer<CBaseAnimatedVariable> m_pSelf;

private:
Memory::CWeakPointer<SAnimationPropertyConfig> m_pConfig;

std::chrono::steady_clock::time_point animationBegin;

bool m_bDummy = true;

CAnimationManager* m_pAnimationManager = nullptr;
bool m_bRemoveEndAfterRan = true;
bool m_bRemoveBeginAfterRan = true;

CallbackFun m_fEndCallback;
CallbackFun m_fBeginCallback;
CallbackFun m_fUpdateCallback;
};

/* This concept represents the minimum requirement for a type to be used with CGenericAnimatedVariable */
template <class ValueImpl>
concept AnimatedType = requires(ValueImpl val) {
requires std::is_copy_constructible_v<ValueImpl>;
{ val == val } -> std::same_as<bool>; // requires operator==
{ val = val }; // requires operator=
};

/*
A generic class for variables.
VarType is the type of the variable to be animated.
AnimationContext is there to attach additional data to the animation.
In Hyprland that struct would contain a reference to window, workspace or layer for example.
*/
template <AnimatedType VarType, class AnimationContext>
class CGenericAnimatedVariable : public CBaseAnimatedVariable {
public:
CGenericAnimatedVariable() = default;

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, pSelf);
}

CGenericAnimatedVariable(const CGenericAnimatedVariable&) = delete;
CGenericAnimatedVariable(CGenericAnimatedVariable&&) = delete;
CGenericAnimatedVariable& operator=(const CGenericAnimatedVariable&) = delete;
CGenericAnimatedVariable& operator=(CGenericAnimatedVariable&&) = delete;

virtual void warp(bool endCallback = true) {
if (!m_bIsBeingAnimated)
return;

m_Value = m_Goal;

m_bIsBeingAnimated = false;

onUpdate();

if (endCallback)
onAnimationEnd();
}

const VarType& value() const {
return m_Value;
}

/* used to update the value each tick via the AnimationManager */
VarType& value() {
return m_Value;
}

const VarType& goal() const {
return m_Goal;
}

const VarType& begun() const {
return m_Begun;
}

CGenericAnimatedVariable& operator=(const VarType& v) {
if (v == m_Goal)
return *this;

m_Goal = v;
m_Begun = m_Value;

onAnimationBegin();

return *this;
}

/* Sets the actual stored value, without affecting the goal, but resets the timer*/
void setValue(const VarType& v) {
if (v == m_Value)
return;

m_Value = v;
m_Begun = m_Value;

onAnimationBegin();
}

/* Sets the actual value and goal*/
void setValueAndWarp(const VarType& v) {
m_Goal = v;
m_bIsBeingAnimated = true;

warp();
}

AnimationContext m_Context;

private:
VarType m_Value{};
VarType m_Goal{};
VarType m_Begun{};
};
}
}
40 changes: 40 additions & 0 deletions include/hyprutils/animation/AnimationManager.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#pragma once

#include "./BezierCurve.hpp"
#include "./AnimatedVariable.hpp"
#include "../math/Vector2D.hpp"
#include "../memory/WeakPtr.hpp"

#include <unordered_map>
#include <vector>

namespace Hyprutils {
namespace Animation {
/* A class for managing bezier curves and variables that are being animated. */
class CAnimationManager {
public:
CAnimationManager();

void tickDone();
bool shouldTickForNext();

virtual void scheduleTick() = 0;
virtual void onTicked() = 0;

void addBezierWithName(std::string, const Math::Vector2D&, const Math::Vector2D&);
void removeAllBeziers();

bool bezierExists(const std::string&);
Memory::CSharedPointer<CBezierCurve> getBezier(const std::string&);

const std::unordered_map<std::string, Memory::CSharedPointer<CBezierCurve>>& getAllBeziers();

std::vector<Memory::CWeakPointer<CBaseAnimatedVariable>> m_vActiveAnimatedVariables;

private:
std::unordered_map<std::string, Memory::CSharedPointer<CBezierCurve>> m_mBezierCurves;

bool m_bTickScheduled = false;
};
}
}
30 changes: 30 additions & 0 deletions include/hyprutils/animation/BezierCurve.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#pragma once

#include <array>
#include <vector>

#include "../math/Vector2D.hpp"

namespace Hyprutils {
namespace Animation {
constexpr int BAKEDPOINTS = 255;
constexpr float INVBAKEDPOINTS = 1.f / BAKEDPOINTS;

/* An implementation of a cubic bezier curve. */
class CBezierCurve {
public:
/* Calculates a cubic bezier curve based on 2 control points (EXCLUDES the 0,0 and 1,1 points). */
void setup(const std::array<Hyprutils::Math::Vector2D, 2>& points);

float getYForT(float const& t) const;
float getXForT(float const& t) const;
float getYForPoint(float const& x) const;

private:
/* this INCLUDES the 0,0 and 1,1 points. */
std::vector<Hyprutils::Math::Vector2D> m_vPoints;

std::array<Hyprutils::Math::Vector2D, BAKEDPOINTS> m_aPointsBaked;
};
}
}
Loading
Loading