oxihuman-morph 0.1.2

Parametric morphology engine for human body generation — targets, blendshapes, FACS
Documentation
#![allow(dead_code)]

#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct ExpressionPhysicsSim {
    jiggle: f32,
    spring: f32,
    damping: f32,
    velocity: f32,
    position: f32,
}

#[allow(dead_code)]
pub fn new_expression_physics(jiggle: f32, spring: f32, damping: f32) -> ExpressionPhysicsSim {
    ExpressionPhysicsSim { jiggle, spring, damping, velocity: 0.0, position: 0.0 }
}

#[allow(dead_code)]
pub fn physics_step_expr(sim: &mut ExpressionPhysicsSim, target: f32, dt: f32) -> f32 {
    let force = -sim.spring * (sim.position - target) - sim.damping * sim.velocity;
    sim.velocity += force * dt;
    sim.position += sim.velocity * dt;
    sim.position += sim.jiggle * sim.velocity.abs() * 0.01;
    sim.position
}

#[allow(dead_code)]
pub fn jiggle_weight(sim: &ExpressionPhysicsSim) -> f32 { sim.jiggle }

#[allow(dead_code)]
pub fn spring_weight(sim: &ExpressionPhysicsSim) -> f32 { sim.spring }

#[allow(dead_code)]
pub fn damping_weight(sim: &ExpressionPhysicsSim) -> f32 { sim.damping }

#[allow(dead_code)]
pub fn physics_reset_expr(sim: &mut ExpressionPhysicsSim) {
    sim.velocity = 0.0; sim.position = 0.0;
}

#[allow(dead_code)]
pub fn physics_to_json_eps(sim: &ExpressionPhysicsSim) -> String {
    format!("{{\"position\":{:.4},\"velocity\":{:.4}}}", sim.position, sim.velocity)
}

#[allow(dead_code)]
pub fn physics_energy(sim: &ExpressionPhysicsSim) -> f32 {
    0.5 * sim.spring * sim.position * sim.position + 0.5 * sim.velocity * sim.velocity
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test] fn test_new() { let s = new_expression_physics(0.1, 10.0, 1.0); assert!((s.position).abs() < 1e-6); }
    #[test] fn test_step() { let mut s = new_expression_physics(0.0, 10.0, 0.5); physics_step_expr(&mut s, 1.0, 0.01); assert!(s.position != 0.0 || s.velocity != 0.0); }
    #[test] fn test_jiggle() { let s = new_expression_physics(0.5, 1.0, 1.0); assert!((jiggle_weight(&s) - 0.5).abs() < 1e-6); }
    #[test] fn test_spring() { let s = new_expression_physics(0.0, 5.0, 1.0); assert!((spring_weight(&s) - 5.0).abs() < 1e-6); }
    #[test] fn test_damping() { let s = new_expression_physics(0.0, 1.0, 3.0); assert!((damping_weight(&s) - 3.0).abs() < 1e-6); }
    #[test] fn test_reset() { let mut s = new_expression_physics(0.0, 10.0, 1.0); physics_step_expr(&mut s, 1.0, 0.1); physics_reset_expr(&mut s); assert!((s.position).abs() < 1e-6); }
    #[test] fn test_json() { let s = new_expression_physics(0.0, 1.0, 1.0); assert!(physics_to_json_eps(&s).contains("position")); }
    #[test] fn test_energy_zero() { let s = new_expression_physics(0.0, 1.0, 1.0); assert!(physics_energy(&s) < 1e-6); }
    #[test] fn test_energy_nonzero() { let mut s = new_expression_physics(0.0, 10.0, 0.1); physics_step_expr(&mut s, 1.0, 0.1); assert!(physics_energy(&s) > 0.0); }
    #[test] fn test_converge() { let mut s = new_expression_physics(0.0, 10.0, 5.0); for _ in 0..1000 { physics_step_expr(&mut s, 0.5, 0.01); } assert!((s.position - 0.5).abs() < 0.5); }
}