#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct CheekRiseConfig {
pub max_rise: f32,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CheekRiseSide {
Left,
Right,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct CheekRiseState {
pub left_rise: f32,
pub right_rise: f32,
}
#[allow(dead_code)]
pub fn default_cheek_rise_config() -> CheekRiseConfig {
CheekRiseConfig { max_rise: 1.0 }
}
#[allow(dead_code)]
pub fn new_cheek_rise_state() -> CheekRiseState {
CheekRiseState {
left_rise: 0.0,
right_rise: 0.0,
}
}
#[allow(dead_code)]
pub fn cr_set_rise(state: &mut CheekRiseState, cfg: &CheekRiseConfig, side: CheekRiseSide, v: f32) {
let clamped = v.clamp(0.0, cfg.max_rise);
match side {
CheekRiseSide::Left => state.left_rise = clamped,
CheekRiseSide::Right => state.right_rise = clamped,
}
}
#[allow(dead_code)]
pub fn cr_set_both(state: &mut CheekRiseState, cfg: &CheekRiseConfig, v: f32) {
let clamped = v.clamp(0.0, cfg.max_rise);
state.left_rise = clamped;
state.right_rise = clamped;
}
#[allow(dead_code)]
pub fn cr_reset(state: &mut CheekRiseState) {
*state = new_cheek_rise_state();
}
#[allow(dead_code)]
pub fn cr_is_neutral(state: &CheekRiseState) -> bool {
state.left_rise.abs() < 1e-6 && state.right_rise.abs() < 1e-6
}
#[allow(dead_code)]
pub fn cr_average(state: &CheekRiseState) -> f32 {
(state.left_rise + state.right_rise) * 0.5
}
#[allow(dead_code)]
pub fn cr_symmetry(state: &CheekRiseState) -> f32 {
(state.left_rise - state.right_rise).abs()
}
#[allow(dead_code)]
pub fn cr_blend(a: &CheekRiseState, b: &CheekRiseState, t: f32) -> CheekRiseState {
let t = t.clamp(0.0, 1.0);
CheekRiseState {
left_rise: a.left_rise + (b.left_rise - a.left_rise) * t,
right_rise: a.right_rise + (b.right_rise - a.right_rise) * t,
}
}
#[allow(dead_code)]
pub fn cr_to_weights(state: &CheekRiseState) -> Vec<(String, f32)> {
vec![
("cheek_rise_l".to_string(), state.left_rise),
("cheek_rise_r".to_string(), state.right_rise),
]
}
#[allow(dead_code)]
pub fn cr_to_json(state: &CheekRiseState) -> String {
format!(
r#"{{"left_rise":{:.4},"right_rise":{:.4}}}"#,
state.left_rise, state.right_rise
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_config() {
let cfg = default_cheek_rise_config();
assert!((cfg.max_rise - 1.0).abs() < 1e-6);
}
#[test]
fn new_state_neutral() {
let s = new_cheek_rise_state();
assert!(cr_is_neutral(&s));
}
#[test]
fn set_rise_left() {
let cfg = default_cheek_rise_config();
let mut s = new_cheek_rise_state();
cr_set_rise(&mut s, &cfg, CheekRiseSide::Left, 0.7);
assert!((s.left_rise - 0.7).abs() < 1e-6);
}
#[test]
fn set_rise_clamps() {
let cfg = default_cheek_rise_config();
let mut s = new_cheek_rise_state();
cr_set_rise(&mut s, &cfg, CheekRiseSide::Right, 2.0);
assert!((s.right_rise - 1.0).abs() < 1e-6);
}
#[test]
fn set_both_symmetric() {
let cfg = default_cheek_rise_config();
let mut s = new_cheek_rise_state();
cr_set_both(&mut s, &cfg, 0.5);
assert!(cr_symmetry(&s) < 1e-6);
}
#[test]
fn average_value() {
let cfg = default_cheek_rise_config();
let mut s = new_cheek_rise_state();
cr_set_rise(&mut s, &cfg, CheekRiseSide::Left, 0.2);
cr_set_rise(&mut s, &cfg, CheekRiseSide::Right, 0.8);
assert!((cr_average(&s) - 0.5).abs() < 1e-6);
}
#[test]
fn reset_clears() {
let cfg = default_cheek_rise_config();
let mut s = new_cheek_rise_state();
cr_set_both(&mut s, &cfg, 0.9);
cr_reset(&mut s);
assert!(cr_is_neutral(&s));
}
#[test]
fn blend_midpoint() {
let a = new_cheek_rise_state();
let cfg = default_cheek_rise_config();
let mut b = new_cheek_rise_state();
cr_set_both(&mut b, &cfg, 1.0);
let m = cr_blend(&a, &b, 0.5);
assert!((m.left_rise - 0.5).abs() < 1e-6);
}
#[test]
fn to_weights_count() {
let s = new_cheek_rise_state();
assert_eq!(cr_to_weights(&s).len(), 2);
}
#[test]
fn to_json_contains_fields() {
let s = new_cheek_rise_state();
let j = cr_to_json(&s);
assert!(j.contains("left_rise"));
}
}