Modules/Math/public/SpringAnimations.hpp
2024-10-14 20:20:21 +03:00

155 lines
No EOL
3.6 KiB
C++

#pragma once
#include "Color.hpp"
#include "Rect.hpp"
#include "Timing.hpp"
namespace tp {
class SpringValue {
public:
SpringValue() = default;
void updateCurrentPosition() {
const auto deltaTime = halnf(gCurrentTime - mPrevTime);
const auto deltaPos = mTargetPosition - mCurrentPosition;
auto deltaVelocity = deltaPos * mSpringStiffness;
deltaVelocity -= mVelocity * (mVelocityDamping);
mVelocity += deltaVelocity;
mCurrentPosition += mVelocity * deltaTime;
mPrevTime = gCurrentTime;
}
void setTargetPosition(halnf pos) {
mTargetPosition = pos;
if (mVelocity == 0) mPrevTime = gCurrentTime;
}
[[nodiscard]] halnf getCurrentPos() const { return mCurrentPosition; }
[[nodiscard]] halnf getTargetPos() const { return mTargetPosition; }
[[nodiscard]] halnf getVelocity() const { return mVelocity; }
[[nodiscard]] halnf getDeltaPos() const { return mTargetPosition - mCurrentPosition; }
void endAnimation() {
mCurrentPosition = mTargetPosition;
mVelocity = 0;
}
private:
time_ms mPrevTime = 0;
halnf mTargetPosition = 0;
halnf mCurrentPosition = 0;
halnf mVelocity = 0;
halnf mSpringStiffness = 0.0041;
halnf mVelocityDamping = 0.25f;
};
class SpringVec {
public:
SpringVec() = default;
void updateCurrentPosition() {
mPosX.updateCurrentPosition();
mPosY.updateCurrentPosition();
}
void setTargetPosition(Vec2F pos) {
mPosX.setTargetPosition(pos.x);
mPosY.setTargetPosition(pos.y);
}
[[nodiscard]] Vec2F getCurrentPos() const {
return { mPosX.getCurrentPos(), mPosY.getCurrentPos() };
}
[[nodiscard]] Vec2F getTargetPos() const {
return { mPosX.getTargetPos(), mPosY.getTargetPos() };
}
[[nodiscard]] Vec2F getVelocity() const {
return { mPosX.getVelocity(), mPosY.getVelocity() };
}
[[nodiscard]] bool checkAnimationShouldEnd() const {
halnf velocityEpsilon = 0.0001f;
halnf positionEpsilon = 1;
const auto vel = abs(mPosY.getVelocity()) > velocityEpsilon || abs(mPosX.getVelocity()) > velocityEpsilon;
const auto pos = abs(mPosY.getDeltaPos()) > positionEpsilon || abs(mPosX.getDeltaPos()) > positionEpsilon;
return !(pos || vel);
}
void endAnimation() {
mPosX.endAnimation();
mPosY.endAnimation();
}
private:
SpringValue mPosY;
SpringValue mPosX;
};
class SpringRect {
public:
SpringRect() = default;
[[nodiscard]] RectF getTargetRect() const {
const auto start = mStartPos.getTargetPos();
const auto end = mEndPos.getTargetPos();
return { start, end - start };
}
[[nodiscard]] RectF getCurrentRect() const {
const auto start = mStartPos.getCurrentPos();
const auto end = mEndPos.getCurrentPos();
return { start, end - start };
}
[[nodiscard]] RGBA getCurrentColor() const {
const auto start = mStartPos.getCurrentPos();
const auto end = mEndPos.getCurrentPos();
return { start.x, start.y, end.x, end.y };
}
void setTargetRect(const RectF& rect) {
mStartPos.setTargetPosition(rect.p1());
mEndPos.setTargetPosition(rect.p3());
}
void setTargetColor(const RGBA& color) {
mStartPos.setTargetPosition({ color.r, color.g });
mEndPos.setTargetPosition({ color.b, color.a });
}
SpringVec& getStart() {
return mStartPos;
}
SpringVec& getEnd() {
return mEndPos;
}
void updateCurrentRect() {
mStartPos.updateCurrentPosition();
mEndPos.updateCurrentPosition();
}
[[nodiscard]] bool shouldEndTransition() const {
return mStartPos.checkAnimationShouldEnd() && mEndPos.checkAnimationShouldEnd();
}
void endAnimation() {
mStartPos.endAnimation();
mEndPos.endAnimation();
}
private:
SpringVec mStartPos;
SpringVec mEndPos;
};
}