Skip to content

Commit

Permalink
animation: add BezierCurve, AnimationManager and AnimatedVariable (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
PaideiaDilemma authored Dec 29, 2024
1 parent 8f15d45 commit 8af7e4b
Show file tree
Hide file tree
Showing 8 changed files with 841 additions and 0 deletions.
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

0 comments on commit 8af7e4b

Please sign in to comment.