From cdb14a76d6772f769a8040399636fed621656705 Mon Sep 17 00:00:00 2001 From: Vladimir Rubin Date: Mon, 9 Jun 2025 20:24:26 +0300 Subject: [PATCH] feat: added more interpolation options added the following interpolation options: - cubic ease-in-out - cosine - exponential --- src/lib.rs | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 79 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7c746a5..3eb3aff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,6 +34,17 @@ pub struct Config { pub disable_timeout: u32, pub gamma_backend: GammaBackend, pub temp_backend: TempBackend, + + pub interpolation_temp: Interpolation, + pub interpolation_gamma: Interpolation, +} + +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum Interpolation { + Linear, + CubicEaseInOut, + Cosine, + Exponential, } #[derive(Debug, PartialEq, Copy, Clone)] @@ -70,6 +81,8 @@ impl Default for Config { notification_timeout: 5000, gamma_backend: GammaBackend::Hyprctl, temp_backend: TempBackend::Hyprctl, + interpolation_temp: Interpolation::Linear, + interpolation_gamma: Interpolation::Linear, } } } @@ -83,6 +96,7 @@ pub enum ConfigError { InvalidGamma(String), InvalidGammaBackend(String), InvalidTempBackend(String), + InvalidInterpolation(String), DuplicateKey(String), } @@ -230,6 +244,20 @@ impl Config { return Err(ConfigError::InvalidTime(value.to_string())); } } + "interpolation_temp" => match value.to_lowercase().as_str() { + "linear" => config.interpolation_temp = Interpolation::Linear, + "cubic" | "cubiceaseinout" => config.interpolation_temp = Interpolation::CubicEaseInOut, + "cosine" => config.interpolation_temp = Interpolation::Cosine, + "exponential" => config.interpolation_temp = Interpolation::Exponential, + _ => return Err(ConfigError::InvalidInterpolation(value.to_string())), + }, + "interpolation_gamma" => match value.to_lowercase().as_str() { + "linear" => config.interpolation_gamma = Interpolation::Linear, + "cubic" | "cubiceaseinout" => config.interpolation_gamma = Interpolation::CubicEaseInOut, + "cosine" => config.interpolation_gamma = Interpolation::Cosine, + "exponential" => config.interpolation_gamma = Interpolation::Exponential, + _ => return Err(ConfigError::InvalidInterpolation(value.to_string())), + }, _ => {} }, _ => {} @@ -256,8 +284,8 @@ impl Config { } /// Linearly interpolate between start and end by factor [0.0, 1.0] -pub fn interpolate(start: u16, end: u16, factor: f64) -> u16 { - trace!("interpolate({start}, {end}, {factor})"); +pub fn interpolate_linear(start: u16, end: u16, factor: f64) -> u16 { + trace!("interpolate_linear({start}, {end}, {factor})"); if end < start { (end as f64 + (start - end) as f64 * (1.0 - factor)).round() as u16 } else { @@ -265,6 +293,51 @@ pub fn interpolate(start: u16, end: u16, factor: f64) -> u16 { } } +/// Cubic interpolation +fn interpolate_cubic(start: u16, end: u16, factor: f64) -> u16 { + trace!("interpolate_cubic({start}, {end}, {factor})"); + let factor = factor.clamp(0.0, 1.0); + // Convert to f64 for arithmetic + let start_f = start as f64; + let end_f = end as f64; + // Cubic ease-in-out: 3t^2 - 2t^3 + let t = factor; + let smooth_t = (3.0 * t * t) - (2.0 * t * t * t); + // Interpolate + let result = start_f + smooth_t * (end_f - start_f); + // Round and clamp to u16 bounds + result.round().max(0.0).min(u16::MAX as f64) as u16 +} + +/// Cosine interpolation +fn interpolate_cosine(start: u16, end: u16, factor: f64) -> u16 { + trace!("interpolate_cosine({start}, {end}, {factor})"); + let t = (1.0 - (factor.clamp(0.0, 1.0) * std::f64::consts::PI).cos()) / 2.0; + interpolate_linear(start, end, t) +} + +fn interpolate_exponential(start: u16, end: u16, factor: f64) -> u16 { + trace!("interpolate_exponential({start}, {end}, {factor})"); + let t = factor.clamp(0.0, 1.0); + let t = if t < 0.5 { + 0.5 * (2.0 * t).powf(3.0) + } else { + 0.5 * (1.0 - (2.0 * (1.0 - t)).powf(3.0)) + 0.5 + }; + interpolate_linear(start, end, t) +} + +/// Interpolation meta-function +fn interpolate_value(start: u16, end: u16, factor: f64, interpolation: &Interpolation) -> u16 { + trace!("interpolate_value({start}, {end}, {factor}, {interpolation:?})"); + match interpolation { + Interpolation::Linear => interpolate_linear(start, end, factor), + Interpolation::CubicEaseInOut => interpolate_cubic(start, end, factor), + Interpolation::Cosine => interpolate_cosine(start, end, factor), + Interpolation::Exponential => interpolate_exponential(start, end, factor), + } +} + /// Compute current temperature and gamma based on provided time pub fn compute_settings(now: Time, config: &Config) -> (u16, u16) { trace!("compute_settings({now:?})"); @@ -278,8 +351,8 @@ pub fn compute_settings(now: Time, config: &Config) -> (u16, u16) { / (config.sunset_end - config.sunset_start) as f64) .clamp(0.0, 1.0); ( - interpolate(config.temp_day, config.temp_night, factor), - interpolate(config.gamma_day, config.gamma_night, factor), + interpolate_value(config.temp_day, config.temp_night, factor, &config.interpolation_temp), + interpolate_value(config.gamma_day, config.gamma_night, factor, &config.interpolation_gamma), ) } else if (time_in_hours >= config.sunrise_start as f64) && (time_in_hours <= config.sunrise_end as f64) @@ -290,8 +363,8 @@ pub fn compute_settings(now: Time, config: &Config) -> (u16, u16) { / (config.sunrise_end - config.sunrise_start) as f64) .clamp(0.0, 1.0); ( - interpolate(config.temp_day, config.temp_night, factor), - interpolate(config.gamma_day, config.gamma_night, factor), + interpolate_value(config.temp_day, config.temp_night, factor, &config.interpolation_temp), + interpolate_value(config.gamma_day, config.gamma_night, factor, &config.interpolation_gamma), ) } else if time_in_hours > config.sunset_end as f64 || time_in_hours < config.sunrise_start as f64