Added bezier curves

This commit is contained in:
vaxerski 2022-04-23 21:47:16 +02:00
parent 306d163613
commit 3ebe7d7972
14 changed files with 255 additions and 67 deletions

View file

@ -50,7 +50,8 @@ Try it out and report bugs / suggestions!
- Easily expandable and readable codebase
- Config reloaded instantly upon saving
- Parabolic window animations
- Bezier-curve window animations
- Custom bezier curves
- Workspaces protocol support
- Tiling/floating/fullscreen windows
- Window/monitor rules

View file

@ -41,7 +41,9 @@ decoration {
animations {
enabled=1
speed=7
speed=7 # speed is measured in 100s of ms, 7 = 700ms
curve=default # you can customize your own bezier curves, see the wiki
windows_curve=default # specific curve for all window animations
windows_speed=6 # specific speeds for components can be made with name_speed=float. 0 means use global (speed=float). If not set, will use the global value.
windows=1
borders=1

View file

@ -158,6 +158,9 @@ void CCompositor::startCompositor() {
Debug::log(LOG, "Creating the KeybindManager!");
g_pKeybindManager = std::make_unique<CKeybindManager>();
Debug::log(LOG, "Creating the AnimationManager!");
g_pAnimationManager = std::make_unique<CAnimationManager>();
Debug::log(LOG, "Creating the ConfigManager!");
g_pConfigManager = std::make_unique<CConfigManager>();
@ -178,9 +181,6 @@ void CCompositor::startCompositor() {
Debug::log(LOG, "Creating the LayoutManager!");
g_pLayoutManager = std::make_unique<CLayoutManager>();
Debug::log(LOG, "Creating the AnimationManager!");
g_pAnimationManager = std::make_unique<CAnimationManager>();
//
//

View file

@ -2,10 +2,10 @@
#include "Compositor.hpp"
CWindow::CWindow() {
m_vRealPosition.create(AVARTYPE_VECTOR, &g_pConfigManager->getConfigValuePtr("animations:windows_speed")->floatValue, &g_pConfigManager->getConfigValuePtr("animations:windows")->intValue, (void*)this);
m_vRealSize.create(AVARTYPE_VECTOR, &g_pConfigManager->getConfigValuePtr("animations:windows_speed")->floatValue, &g_pConfigManager->getConfigValuePtr("animations:windows")->intValue, (void*)this);
m_cRealBorderColor.create(AVARTYPE_COLOR, &g_pConfigManager->getConfigValuePtr("animations:borders_speed")->floatValue, &g_pConfigManager->getConfigValuePtr("animations:borders")->intValue, (void*)this);
m_fAlpha.create(AVARTYPE_FLOAT, &g_pConfigManager->getConfigValuePtr("animations:fadein_speed")->floatValue, &g_pConfigManager->getConfigValuePtr("animations:fadein")->intValue, (void*)this);
m_vRealPosition.create(AVARTYPE_VECTOR, &g_pConfigManager->getConfigValuePtr("animations:windows_speed")->floatValue, &g_pConfigManager->getConfigValuePtr("animations:windows")->intValue, &g_pConfigManager->getConfigValuePtr("animations:windows_curve")->strValue, (void*) this);
m_vRealSize.create(AVARTYPE_VECTOR, &g_pConfigManager->getConfigValuePtr("animations:windows_speed")->floatValue, &g_pConfigManager->getConfigValuePtr("animations:windows")->intValue, &g_pConfigManager->getConfigValuePtr("animations:windows_curve")->strValue, (void*)this);
m_cRealBorderColor.create(AVARTYPE_COLOR, &g_pConfigManager->getConfigValuePtr("animations:borders_speed")->floatValue, &g_pConfigManager->getConfigValuePtr("animations:borders")->intValue, &g_pConfigManager->getConfigValuePtr("animations:borders_curve")->strValue, (void*)this);
m_fAlpha.create(AVARTYPE_FLOAT, &g_pConfigManager->getConfigValuePtr("animations:fadein_speed")->floatValue, &g_pConfigManager->getConfigValuePtr("animations:fadein")->intValue, &g_pConfigManager->getConfigValuePtr("animations:fadein_curve")->strValue, (void*)this);
}
CWindow::~CWindow() {

View file

@ -43,10 +43,14 @@ void CConfigManager::setDefaultVars() {
configValues["animations:enabled"].intValue = 1;
configValues["animations:speed"].floatValue = 7.f;
configValues["animations:curve"].strValue = "default";
configValues["animations:windows_curve"].strValue = "[[f]]";
configValues["animations:windows_speed"].floatValue = 0.f;
configValues["animations:windows"].intValue = 1;
configValues["animations:borders_curve"].strValue = "[[f]]";
configValues["animations:borders_speed"].floatValue = 0.f;
configValues["animations:borders"].intValue = 1;
configValues["animations:fadein_curve"].strValue = "[[f]]";
configValues["animations:fadein_speed"].floatValue = 0.f;
configValues["animations:fadein"].intValue = 1;
@ -207,6 +211,39 @@ void CConfigManager::handleMonitor(const std::string& command, const std::string
m_dMonitorRules.push_back(newrule);
}
void CConfigManager::handleBezier(const std::string& command, const std::string& args) {
std::string curitem = "";
std::string argZ = args;
auto nextItem = [&]() {
auto idx = argZ.find_first_of(',');
if (idx != std::string::npos) {
curitem = argZ.substr(0, idx);
argZ = argZ.substr(idx + 1);
} else {
curitem = argZ;
argZ = "";
}
};
nextItem();
std::string bezierName = curitem;
nextItem();
float p1x = std::stof(curitem);
nextItem();
float p1y = std::stof(curitem);
nextItem();
float p2x = std::stof(curitem);
nextItem();
float p2y = std::stof(curitem);
g_pAnimationManager->addBezierWithName(bezierName, Vector2D(p1x, p1y), Vector2D(p2x, p2y));
}
void CConfigManager::handleBind(const std::string& command, const std::string& value) {
// example:
// bind=SUPER,G,exec,dmenu_run <args>
@ -311,6 +348,8 @@ std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std::
handleDefaultWorkspace(COMMAND, VALUE);
} else if (COMMAND == "windowrule") {
handleWindowRule(COMMAND, VALUE);
} else if (COMMAND == "bezier") {
handleBezier(COMMAND, VALUE);
} else {
configSetValueSafe(currentCategory + (currentCategory == "" ? "" : ":") + COMMAND, VALUE);
}
@ -379,10 +418,10 @@ void CConfigManager::loadConfigLoadVars() {
// reset all vars before loading
setDefaultVars();
m_dMonitorRules.clear();
m_dWindowRules.clear();
g_pKeybindManager->clearKeybinds();
g_pAnimationManager->removeAllBeziers();
const char* const ENVHOME = getenv("HOME");
const std::string CONFIGPATH = ENVHOME + (ISDEBUG ? (std::string) "/.config/hypr/hyprlandd.conf" : (std::string) "/.config/hypr/hyprland.conf");

View file

@ -92,6 +92,7 @@ private:
void handleUnbind(const std::string&, const std::string&);
void handleWindowRule(const std::string&, const std::string&);
void handleDefaultWorkspace(const std::string&, const std::string&);
void handleBezier(const std::string&, const std::string&);
};
inline std::unique_ptr<CConfigManager> g_pConfigManager;

View file

@ -5,19 +5,20 @@ CAnimatedVariable::CAnimatedVariable() {
; // dummy var
}
void CAnimatedVariable::create(ANIMATEDVARTYPE type, float* speed, int64_t* enabled, void* pWindow) {
void CAnimatedVariable::create(ANIMATEDVARTYPE type, float* speed, int64_t* enabled, std::string* pBezier, void* pWindow) {
m_eVarType = type;
m_pSpeed = speed;
m_pEnabled = enabled;
m_pWindow = pWindow;
m_pBezier = pBezier;
g_pAnimationManager->m_lAnimatedVariables.push_back(this);
m_bDummy = false;
}
void CAnimatedVariable::create(ANIMATEDVARTYPE type, std::any val, float* speed, int64_t* enabled, void* pWindow) {
create(type, speed, enabled, pWindow);
void CAnimatedVariable::create(ANIMATEDVARTYPE type, std::any val, float* speed, int64_t* enabled, std::string* pBezier, void* pWindow) {
create(type, speed, enabled, pBezier, pWindow);
try {
switch (type) {

View file

@ -16,8 +16,8 @@ class CAnimatedVariable {
public:
CAnimatedVariable(); // dummy var
void create(ANIMATEDVARTYPE, float* speed, int64_t* enabled, void* pWindow);
void create(ANIMATEDVARTYPE, std::any val, float* speed, int64_t* enabled, void* pWindow);
void create(ANIMATEDVARTYPE, float* speed, int64_t* enabled, std::string* pBezier, void* pWindow);
void create(ANIMATEDVARTYPE, std::any val, float* speed, int64_t* enabled, std::string* pBezier, void* pWindow);
~CAnimatedVariable();
@ -60,34 +60,46 @@ public:
void operator=(const Vector2D& v) {
ASSERT(m_eVarType == AVARTYPE_VECTOR);
m_vGoal = v;
animationBegin = std::chrono::system_clock::now();
m_vBegun = m_vValue;
}
void operator=(const float& v) {
ASSERT(m_eVarType == AVARTYPE_FLOAT);
m_fGoal = v;
animationBegin = std::chrono::system_clock::now();
m_fBegun = m_fValue;
}
void operator=(const CColor& v) {
ASSERT(m_eVarType == AVARTYPE_COLOR);
m_cGoal = v;
animationBegin = std::chrono::system_clock::now();
m_cBegun = m_cValue;
}
// Sets the actual stored value, without affecting the goal
// Sets the actual stored value, without affecting the goal, but resets the timer
void setValue(const Vector2D& v) {
ASSERT(m_eVarType == AVARTYPE_VECTOR);
m_vValue = v;
animationBegin = std::chrono::system_clock::now();
m_vBegun = m_vValue;
}
// Sets the actual stored value, without affecting the goal
// Sets the actual stored value, without affecting the goal, but resets the timer
void setValue(const float& v) {
ASSERT(m_eVarType == AVARTYPE_FLOAT);
m_fValue = v;
animationBegin = std::chrono::system_clock::now();
m_vBegun = m_vValue;
}
// Sets the actual stored value, without affecting the goal
// Sets the actual stored value, without affecting the goal, but resets the timer
void setValue(const CColor& v) {
ASSERT(m_eVarType == AVARTYPE_COLOR);
m_cValue = v;
animationBegin = std::chrono::system_clock::now();
m_vBegun = m_vValue;
}
// Sets the actual value and goal
@ -140,12 +152,19 @@ private:
float m_fGoal = 0;
CColor m_cGoal;
Vector2D m_vBegun = Vector2D(0,0);
float m_fBegun = 0;
CColor m_cBegun;
float* m_pSpeed = nullptr;
int64_t* m_pEnabled = nullptr;
void* m_pWindow = nullptr;
std::string* m_pBezier = nullptr;
bool m_bDummy = true;
std::chrono::system_clock::time_point animationBegin;
ANIMATEDVARTYPE m_eVarType = AVARTYPE_INVALID;
friend class CAnimationManager;

View file

@ -0,0 +1,55 @@
#include "BezierCurve.hpp"
void CBezierCurve::setup(std::vector<Vector2D>* pVec) {
m_dPoints.clear();
m_dPoints.emplace_back(Vector2D(0,0));
for (auto& p : *pVec) {
m_dPoints.push_back(p);
}
m_dPoints.emplace_back(Vector2D(1,1));
RASSERT(m_dPoints.size() == 4, "CBezierCurve only supports cubic beziers! (points num: %i)", m_dPoints.size());
// bake 100 points for faster lookups
// T -> X ( / 100 )
for (int i = 0; i < 100; ++i) {
m_aPointsBaked[i] = getXForT((i + 1) / 100.f);
}
}
float CBezierCurve::getYForT(float t) {
return 3 * t * pow(1 - t, 2) * m_dPoints[1].y + 3 * pow(t, 2) * (1 - t) * m_dPoints[2].y + pow(t, 3);
}
float CBezierCurve::getXForT(float t) {
return 3 * t * pow(1 - t, 2) * m_dPoints[1].x + 3 * pow(t, 2) * (1 - t) * m_dPoints[2].x + pow(t, 3);
}
// Todo: this probably can be done better and faster
float CBezierCurve::getYForPoint(float x) {
// binary search for the range UPDOWN X
float upperX = 1;
float lowerX = 0;
float mid = 0.5;
while(std::abs(upperX - lowerX) > 0.01f) {
if (m_aPointsBaked[((int)(mid * 100.f))] > x) {
upperX = mid;
} else {
lowerX = mid;
}
mid = (upperX + lowerX) / 2.f;
}
// in the name of performance i shall make a hack
const auto PERCINDELTA = (x - m_aPointsBaked[(int)(100.f * lowerX)]) / (m_aPointsBaked[(int)(100.f * upperX)] - m_aPointsBaked[(int)(100.f * lowerX)]);
if (std::isnan(PERCINDELTA) || std::isinf(PERCINDELTA)) // can sometimes happen for VERY small x
return 0.f;
return getYForT(mid + PERCINDELTA * 0.01f);
}

View file

@ -0,0 +1,24 @@
#pragma once
#include "../defines.hpp"
#include <deque>
// an implementation of a cubic bezier curve
// might do better later
// TODO: n-point curves
class CBezierCurve {
public:
// sets up the bezier curve.
// this EXCLUDES the 0,0 and 1,1 points,
void setup(std::vector<Vector2D>* points);
float getYForT(float t);
float getXForT(float t);
float getYForPoint(float x);
private:
// this INCLUDES the 0,0 and 1,1 points.
std::deque<Vector2D> m_dPoints;
std::array<float, 100> m_aPointsBaked;
};

View file

@ -11,5 +11,17 @@ public:
float r = 0, g = 0, b = 0, a = 255;
uint64_t getAsHex();
CColor operator- (const CColor& c2) const {
return CColor(r - c2.r, g - c2.g, b - c2.b, a - c2.a);
}
CColor operator+ (const CColor& c2) const {
return CColor(r + c2.r, g + c2.g, b + c2.b, a + c2.a);
}
CColor operator* (const float& v) const {
return CColor(r * v, g * v, b * v, a * v);
}
};

View file

@ -451,10 +451,10 @@ void CHyprDwindleLayout::onMouseMove(const Vector2D& mousePos) {
DRAGGINGWINDOW->m_vRealPosition.setValueAndWarp(m_vBeginDragPositionXY + DELTA);
} else {
if (DRAGGINGWINDOW->m_bIsFloating) {
DRAGGINGWINDOW->m_vRealSize.setValueAndWarp(m_vBeginDragSizeXY + DELTA);
DRAGGINGWINDOW->m_vRealPosition.setValueAndWarp(m_vBeginDragSizeXY + DELTA);
DRAGGINGWINDOW->m_vRealSize.setValueAndWarp(Vector2D(std::clamp(DRAGGINGWINDOW->m_vRealSize.vec().x, (double)20, (double)999999), std::clamp(DRAGGINGWINDOW->m_vRealSize.vec().y, (double)20, (double)999999)));
g_pXWaylandManager->setWindowSize(DRAGGINGWINDOW, DRAGGINGWINDOW->m_vRealSize.vec());
g_pXWaylandManager->setWindowSize(DRAGGINGWINDOW, DRAGGINGWINDOW->m_vRealSize.goalv());
} else {
// we need to adjust the splitratio

View file

@ -1,6 +1,24 @@
#include "AnimationManager.hpp"
#include "../Compositor.hpp"
CAnimationManager::CAnimationManager() {
std::vector<Vector2D> points = {Vector2D(0, 0.75f), Vector2D(0.25f, 1.f)};
m_mBezierCurves["default"].setup(&points);
}
void CAnimationManager::removeAllBeziers() {
m_mBezierCurves.clear();
// add the default one
std::vector<Vector2D> points = {Vector2D(0, 0.75f), Vector2D(0.25f, 1.f)};
m_mBezierCurves["default"].setup(&points);
}
void CAnimationManager::addBezierWithName(std::string name, const Vector2D& p1, const Vector2D& p2) {
std::vector points = {p1, p2};
m_mBezierCurves[name].setup(&points);
}
void CAnimationManager::tick() {
bool animationsDisabled = false;
@ -9,61 +27,88 @@ void CAnimationManager::tick() {
animationsDisabled = true;
const float ANIMSPEED = g_pConfigManager->getFloat("animations:speed");
const auto BORDERSIZE = g_pConfigManager->getInt("general:border_size");
const auto BEZIERSTR = g_pConfigManager->getString("animations:curve");
auto DEFAULTBEZIER = m_mBezierCurves.find(BEZIERSTR);
if (DEFAULTBEZIER == m_mBezierCurves.end())
DEFAULTBEZIER = m_mBezierCurves.find("default");
for (auto& av : m_lAnimatedVariables) {
// first, we check if it's disabled, if so, warp
if (av->m_pEnabled == 0 || animationsDisabled) {
av->warp();
continue;
}
// get speed
const auto SPEED = *av->m_pSpeed == 0 ? ANIMSPEED : *av->m_pSpeed;
// window stuff
const auto PWINDOW = (CWindow*)av->m_pWindow;
bool needsDamage = false;
wlr_box WLRBOXPREV = {PWINDOW->m_vRealPosition.vec().x - BORDERSIZE - 1, PWINDOW->m_vRealPosition.vec().y - BORDERSIZE - 1, PWINDOW->m_vRealSize.vec().x + 2 * BORDERSIZE + 2, PWINDOW->m_vRealSize.vec().y + 2 * BORDERSIZE + 2};
// TODO: curves
// check if it's disabled, if so, warp
if (av->m_pEnabled == 0 || animationsDisabled) {
av->warp();
g_pHyprRenderer->damageBox(&WLRBOXPREV);
g_pHyprRenderer->damageWindow(PWINDOW);
continue;
}
// parabolic with a switch unforto
// beziers are with a switch unforto
// TODO: maybe do something cleaner
// get the spent % (0 - 1)
const auto DURATIONPASSED = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - av->animationBegin).count();
const float SPENT = std::clamp((DURATIONPASSED / 100.f) / SPEED, 0.f, 1.f);
switch (av->m_eVarType) {
case AVARTYPE_FLOAT: {
if (!deltazero(av->m_fValue, av->m_fGoal)) {
if (deltaSmallToFlip(av->m_fValue, av->m_fGoal)) {
av->warp();
} else {
av->m_fValue = parabolic(av->m_fValue, av->m_fGoal, SPEED);
}
const auto DELTA = av->m_fGoal - av->m_fBegun;
const auto BEZIER = m_mBezierCurves.find(*av->m_pBezier);
needsDamage = true;
if (BEZIER != m_mBezierCurves.end())
av->m_fValue = av->m_fBegun + BEZIER->second.getYForPoint(SPENT) * DELTA;
else
av->m_fValue = av->m_fBegun + DEFAULTBEZIER->second.getYForPoint(SPENT) * DELTA;
if (SPENT >= 1.f) {
av->warp();
}
} else {
continue; // dont process
}
break;
}
case AVARTYPE_VECTOR: {
if (!deltazero(av->m_vValue, av->m_vGoal)) {
if (deltaSmallToFlip(av->m_vValue, av->m_vGoal)) {
const auto DELTA = av->m_vGoal - av->m_vBegun;
const auto BEZIER = m_mBezierCurves.find(*av->m_pBezier);
if (BEZIER != m_mBezierCurves.end())
av->m_vValue = av->m_vBegun + DELTA * BEZIER->second.getYForPoint(SPENT);
else
av->m_vValue = av->m_vBegun + DELTA * DEFAULTBEZIER->second.getYForPoint(SPENT);
if (SPENT >= 1.f) {
av->warp();
} else {
av->m_vValue.x = parabolic(av->m_vValue.x, av->m_vGoal.x, SPEED);
av->m_vValue.y = parabolic(av->m_vValue.y, av->m_vGoal.y, SPEED);
}
needsDamage = true;
} else {
continue; // dont process
}
break;
}
case AVARTYPE_COLOR: {
if (!deltazero(av->m_cValue, av->m_cGoal)) {
if (deltaSmallToFlip(av->m_cValue, av->m_cGoal)) {
const auto DELTA = av->m_cGoal - av->m_cBegun;
const auto BEZIER = m_mBezierCurves.find(*av->m_pBezier);
if (BEZIER != m_mBezierCurves.end())
av->m_cValue = av->m_cBegun + DELTA * BEZIER->second.getYForPoint(SPENT);
else
av->m_cValue = av->m_cBegun + DELTA * DEFAULTBEZIER->second.getYForPoint(SPENT);
if (SPENT >= 1.f) {
av->warp();
} else {
av->m_cValue = parabolic(SPEED, av->m_cValue, av->m_cGoal);
}
needsDamage = true;
} else {
continue; // dont process
}
break;
}
@ -72,11 +117,9 @@ void CAnimationManager::tick() {
}
}
// invalidate the window
if (needsDamage) {
g_pHyprRenderer->damageBox(&WLRBOXPREV);
g_pHyprRenderer->damageWindow(PWINDOW);
}
// damage the window
g_pHyprRenderer->damageBox(&WLRBOXPREV);
g_pHyprRenderer->damageWindow(PWINDOW);
}
}
@ -102,19 +145,4 @@ bool CAnimationManager::deltazero(const float& a, const float& b) {
bool CAnimationManager::deltazero(const CColor& a, const CColor& b) {
return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
}
double CAnimationManager::parabolic(const double from, const double to, const double incline) {
return from + ((to - from) / incline);
}
CColor CAnimationManager::parabolic(const double incline, const CColor& from, const CColor& to) {
CColor newColor;
newColor.r = parabolic(from.r, to.r, incline);
newColor.g = parabolic(from.g, to.g, incline);
newColor.b = parabolic(from.b, to.b, incline);
newColor.a = parabolic(from.a, to.a, incline);
return newColor;
}

View file

@ -2,12 +2,18 @@
#include "../defines.hpp"
#include <list>
#include <unordered_map>
#include "../helpers/AnimatedVariable.hpp"
#include "../helpers/BezierCurve.hpp"
class CAnimationManager {
public:
CAnimationManager();
void tick();
void addBezierWithName(std::string, const Vector2D&, const Vector2D&);
void removeAllBeziers();
std::list<CAnimatedVariable*> m_lAnimatedVariables;
@ -18,8 +24,8 @@ private:
bool deltazero(const Vector2D& a, const Vector2D& b);
bool deltazero(const CColor& a, const CColor& b);
bool deltazero(const float& a, const float& b);
double parabolic(const double, const double, const double);
CColor parabolic(const double, const CColor&, const CColor&);
std::unordered_map<std::string, CBezierCurve> m_mBezierCurves;
};
inline std::unique_ptr<CAnimationManager> g_pAnimationManager;