feat: cubic-bezier curve for animation (#2059)

* feat: bezier curve for animation

* fixes

---------

Co-authored-by: Ivan Molodetskikh <yalterz@gmail.com>
This commit is contained in:
Horu
2025-08-29 15:31:50 +10:00
committed by GitHub
parent d9833fc1c3
commit 1ffda91e0c
5 changed files with 181 additions and 6 deletions
+60
View File
@@ -0,0 +1,60 @@
use keyframe::EasingFunction;
#[derive(Debug, Clone, Copy)]
pub struct CubicBezier {
x1: f64,
y1: f64,
x2: f64,
y2: f64,
}
impl CubicBezier {
pub fn new(x1: f64, y1: f64, x2: f64, y2: f64) -> Self {
Self { x1, y1, x2, y2 }
}
// Based on libadwaita (LGPL-2.1-or-later):
// https://gitlab.gnome.org/GNOME/libadwaita/-/blob/1.7.6/src/adw-easing.c?ref_type=tags#L469-531
fn x_for_t(&self, t: f64) -> f64 {
let omt = 1. - t;
3. * omt * omt * t * self.x1 + 3. * omt * t * t * self.x2 + t * t * t
}
fn y_for_t(&self, t: f64) -> f64 {
let omt = 1. - t;
3. * omt * omt * t * self.y1 + 3. * omt * t * t * self.y2 + t * t * t
}
fn t_for_x(&self, x: f64) -> f64 {
let mut min_t = 0.;
let mut max_t = 1.;
for _ in 0..=30 {
let guess_t = (min_t + max_t) / 2.;
let guess_x = self.x_for_t(guess_t);
if x < guess_x {
max_t = guess_t;
} else {
min_t = guess_t;
}
}
(min_t + max_t) / 2.
}
}
impl EasingFunction for CubicBezier {
fn y(&self, x: f64) -> f64 {
if x <= f64::EPSILON {
return 0.;
}
if 1. - f64::EPSILON <= x {
return 1.;
}
self.y_for_t(self.t_for_x(x))
}
}
+8
View File
@@ -3,6 +3,9 @@ use std::time::Duration;
use keyframe::functions::{EaseOutCubic, EaseOutQuad};
use keyframe::EasingFunction;
mod bezier;
use bezier::CubicBezier;
mod spring;
pub use spring::{Spring, SpringParams};
@@ -43,6 +46,7 @@ pub enum Curve {
EaseOutQuad,
EaseOutCubic,
EaseOutExpo,
CubicBezier(CubicBezier),
}
impl Animation {
@@ -342,6 +346,7 @@ impl Curve {
Curve::EaseOutQuad => EaseOutQuad.y(x),
Curve::EaseOutCubic => EaseOutCubic.y(x),
Curve::EaseOutExpo => 1. - 2f64.powf(-10. * x),
Curve::CubicBezier(b) => b.y(x),
}
}
}
@@ -353,6 +358,9 @@ impl From<niri_config::animations::Curve> for Curve {
niri_config::animations::Curve::EaseOutQuad => Curve::EaseOutQuad,
niri_config::animations::Curve::EaseOutCubic => Curve::EaseOutCubic,
niri_config::animations::Curve::EaseOutExpo => Curve::EaseOutExpo,
niri_config::animations::Curve::CubicBezier(x1, y1, x2, y2) => {
Curve::CubicBezier(CubicBezier::new(x1, y1, x2, y2))
}
}
}
}