diff --git a/src/tui/component/easing.cppm b/src/tui/component/easing.cppm new file mode 100644 index 0000000..b6259c9 --- /dev/null +++ b/src/tui/component/easing.cppm @@ -0,0 +1,201 @@ +/** + * @file tui/component/easing.cppm + * @brief 缓动函数 + * @attention 魔数有点多,不过也算使用比较广泛了 + * @author KeqingMoe + * @date 2024-12-9 + * @copyright Copyright (c) 2024 KeqingMoe + * @license MIT License + */ + +export module kqm.tui.component:easing; + +import std; + +constexpr auto k_pi(std::floating_point auto x) noexcept +{ + return std::numbers::pi_v * x; +} + +constexpr auto bounce_out(std::floating_point auto x) noexcept +{ + using float_t = decltype(x); + if (x < float_t{4} / float_t{11}) return (121 * x * x) / 16; + if (x < float_t{8} / float_t{11}) return 9.075 * x * x + 9.9 * x + 3.4; + if (x < 0.9) return (4356 * x * x / 361) - (35442 * x / 1805) + float_t{16061} / float_t{1805}; + return 10.8 * x * x - 20.52 * x + 10.72; +} + +export namespace kqm::tui +{ + +inline namespace component +{ + +/** + * @brief 缓动函数 + */ +inline namespace easing +{ + +/** + * @brief 概念 `EasingFunctionsWithFloat` + * @details 用于实现 `EasingFunctions` 概念 + * @tparam EF 缓动函数 + * @tparam Float 浮点数类型,EF 要能够与它一同工作 + */ +template +concept EasingFunctionsWithFloat = requires(EF&& ease, Float x) { + requires std::floating_point; + { ease.in(x) } -> std::convertible_to; + { ease.out(x) } -> std::convertible_to; + { ease.in_out(x) } -> std::convertible_to; +}; + +/** + * @brief 概念 `EasingFunctions` + * @details 约束“缓动函数”概念 + * @tparam EF 缓动函数 + */ +template +concept EasingFunctions = requires { + requires EasingFunctionsWithFloat; + requires EasingFunctionsWithFloat; + requires EasingFunctionsWithFloat; +}; + +/** + * @brief 缓动函数 + * @details 基于静态多态,派生类需满足 `EasingFunctions` 概念 + */ +struct ease_base +{ + constexpr auto out(this EasingFunctions auto&& self, std::floating_point auto x) + noexcept(noexcept(self.in(x))) -> decltype(x) + { + return 1 - self.in(1 - x); + } + constexpr auto in_out(this EasingFunctions auto&& self, std::floating_point auto x) + noexcept(noexcept(self.in(x)) && noexcept(self.out(x))) -> decltype(x) + { + if (x < 0.5) return 0.5 * self.in(2 * x); + return 0.5 * (self.out(2 * (x - 1)) + 1); + } +}; + +/** + * @brief N 次缓动函数 + * @tparam N 指数 + */ +template + requires(N > 0) +struct pow : ease_base +{ + constexpr auto in(std::floating_point auto x) noexcept + { + auto result = x; + for (auto i = 1uz; i < N; ++i) { + result *= x; + } + return result; + } +}; + + +/** + * @brief 线性缓动函数 + */ +using linear = pow<1uz>; + +/** + * @brief 二次缓动函数 + */ +using quadratic = pow<2uz>; + +/** + * @brief 三次缓动函数 + */ +using cubic = pow<3uz>; + +/** + * @brief 四次缓动函数 + */ +using quartic = pow<4uz>; + +/** + * @brief 五次缓动函数 + */ +using quintic = pow<4uz>; + + +/** + * @brief 正弦缓动函数 + */ +struct sin : ease_base +{ + constexpr auto in(std::floating_point auto x) noexcept + { + return std::sin(0.5 * k_pi(x - 1)) + 1; + } + constexpr auto in_out(std::floating_point auto x) noexcept + { + return 0.5 * (1 - std::cos(k_pi(x))); + } +}; + + +/** + * @brief 圆形缓动函数 + */ +struct circular : ease_base +{ + constexpr auto in(std::floating_point auto x) noexcept + { + return 1 - std::sqrt(1 - x * x); + } +}; + + +/** + * @brief 弹性 (Elastic) 缓动函数 + */ +struct elastic : ease_base +{ + constexpr auto in(std::floating_point auto x) noexcept + { + return std::sin(6.5 * k_pi(x)) * std::pow(2, 10 * (x - 1)); + } +}; + +/** + * @brief 向后 (Back) 缓动函数 + */ +struct back : ease_base +{ + constexpr auto in(std::floating_point auto x) noexcept + { + return x * x * x - x * std::pow(k_pi(x)); + } +}; + +/** + * @brief 弹跳 (Bounce) 缓动函数 + */ +struct bounce : ease_base +{ + constexpr auto in(std::floating_point auto x) noexcept + { + return 1 - bounce_out(1 - x); + } + constexpr auto out(std::floating_point auto x) noexcept + { + return bounce_out(x); + } +}; + + +} + +} + +}