#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct MouthShapeConfig {
pub mouth_width: f32,
pub lip_thickness: f32,
pub mouth_depth: f32,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct MouthShapeState {
pub width: f32,
pub open: f32,
pub upper_lip: f32,
pub lower_lip: f32,
pub pout: f32,
pub dimple: f32,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct MouthShapeMorphWeights {
pub wide: f32,
pub narrow: f32,
pub open: f32,
pub closed: f32,
pub upper_thick: f32,
pub lower_thick: f32,
}
#[allow(dead_code)]
pub fn default_mouth_shape_config() -> MouthShapeConfig {
MouthShapeConfig {
mouth_width: 1.0,
lip_thickness: 1.0,
mouth_depth: 1.0,
}
}
#[allow(dead_code)]
pub fn new_mouth_shape_state() -> MouthShapeState {
MouthShapeState {
width: 0.0,
open: 0.0,
upper_lip: 0.0,
lower_lip: 0.0,
pout: 0.0,
dimple: 0.0,
}
}
#[allow(dead_code)]
pub fn set_mouth_width(state: &mut MouthShapeState, width: f32) {
state.width = width.clamp(-1.0, 1.0);
}
#[allow(dead_code)]
pub fn set_mouth_open(state: &mut MouthShapeState, open: f32) {
state.open = open.clamp(0.0, 1.0);
}
#[allow(dead_code)]
pub fn set_upper_lip(state: &mut MouthShapeState, thickness: f32) {
state.upper_lip = thickness.clamp(-1.0, 1.0);
}
#[allow(dead_code)]
pub fn set_lower_lip(state: &mut MouthShapeState, thickness: f32) {
state.lower_lip = thickness.clamp(-1.0, 1.0);
}
#[allow(dead_code)]
pub fn set_pout(state: &mut MouthShapeState, pout: f32) {
state.pout = pout.clamp(0.0, 1.0);
}
#[allow(dead_code)]
pub fn compute_mouth_shape_weights(
state: &MouthShapeState,
cfg: &MouthShapeConfig,
) -> MouthShapeMorphWeights {
let width_factor = cfg.mouth_width.max(0.001);
let thick_factor = cfg.lip_thickness.max(0.001);
let wide = (state.width * width_factor).clamp(0.0, 1.0);
let narrow = ((-state.width) * width_factor).clamp(0.0, 1.0);
let open = (state.open * cfg.mouth_depth.max(0.001)).clamp(0.0, 1.0);
let closed = 1.0 - open;
let upper_thick = (state.upper_lip * thick_factor).clamp(0.0, 1.0);
let lower_thick = (state.lower_lip * thick_factor).clamp(0.0, 1.0);
MouthShapeMorphWeights {
wide,
narrow,
open,
closed,
upper_thick,
lower_thick,
}
}
#[allow(dead_code)]
pub fn blend_mouth_shapes(
a: &MouthShapeState,
b: &MouthShapeState,
t: f32,
) -> MouthShapeState {
let t = t.clamp(0.0, 1.0);
let s = 1.0 - t;
MouthShapeState {
width: a.width * s + b.width * t,
open: a.open * s + b.open * t,
upper_lip: a.upper_lip * s + b.upper_lip * t,
lower_lip: a.lower_lip * s + b.lower_lip * t,
pout: a.pout * s + b.pout * t,
dimple: a.dimple * s + b.dimple * t,
}
}
#[allow(dead_code)]
pub fn reset_mouth_shape(state: &mut MouthShapeState) {
*state = new_mouth_shape_state();
}
#[allow(dead_code)]
pub fn mouth_shape_to_json(state: &MouthShapeState) -> String {
format!(
r#"{{"width":{:.4},"open":{:.4},"upper_lip":{:.4},"lower_lip":{:.4},"pout":{:.4},"dimple":{:.4}}}"#,
state.width, state.open, state.upper_lip, state.lower_lip, state.pout, state.dimple
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let cfg = default_mouth_shape_config();
assert!((cfg.mouth_width - 1.0).abs() < 1e-6);
assert!((cfg.lip_thickness - 1.0).abs() < 1e-6);
assert!((cfg.mouth_depth - 1.0).abs() < 1e-6);
}
#[test]
fn test_new_state_zeroed() {
let s = new_mouth_shape_state();
assert_eq!(s.width, 0.0);
assert_eq!(s.open, 0.0);
assert_eq!(s.pout, 0.0);
}
#[test]
fn test_set_clamping() {
let mut s = new_mouth_shape_state();
set_mouth_width(&mut s, 5.0);
assert!((s.width - 1.0).abs() < 1e-6);
set_mouth_open(&mut s, -2.0);
assert_eq!(s.open, 0.0);
set_pout(&mut s, 2.0);
assert!((s.pout - 1.0).abs() < 1e-6);
}
#[test]
fn test_compute_weights() {
let cfg = default_mouth_shape_config();
let mut s = new_mouth_shape_state();
set_mouth_width(&mut s, 1.0);
set_mouth_open(&mut s, 0.5);
let w = compute_mouth_shape_weights(&s, &cfg);
assert!((w.wide - 1.0).abs() < 1e-6);
assert_eq!(w.narrow, 0.0);
assert!((w.open - 0.5).abs() < 1e-6);
}
#[test]
fn test_blend() {
let mut a = new_mouth_shape_state();
set_mouth_open(&mut a, 0.0);
let mut b = new_mouth_shape_state();
set_mouth_open(&mut b, 1.0);
let mid = blend_mouth_shapes(&a, &b, 0.5);
assert!((mid.open - 0.5).abs() < 1e-6);
}
#[test]
fn test_reset() {
let mut s = new_mouth_shape_state();
set_mouth_open(&mut s, 1.0);
set_pout(&mut s, 1.0);
reset_mouth_shape(&mut s);
assert_eq!(s.open, 0.0);
assert_eq!(s.pout, 0.0);
}
#[test]
fn test_to_json() {
let s = new_mouth_shape_state();
let j = mouth_shape_to_json(&s);
assert!(j.contains("\"width\""));
assert!(j.contains("\"open\""));
assert!(j.contains("\"pout\""));
}
}