#![allow(dead_code)]
use std::collections::HashMap;
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct FaceProfile {
pub name: String,
pub params: HashMap<String, f32>,
}
#[allow(dead_code)]
pub fn new_face_profile(name: &str) -> FaceProfile {
FaceProfile {
name: name.to_string(),
params: HashMap::new(),
}
}
#[allow(dead_code)]
pub fn profile_set_param(profile: &mut FaceProfile, key: &str, value: f32) {
profile.params.insert(key.to_string(), value);
}
#[allow(dead_code)]
pub fn profile_get_param(profile: &FaceProfile, key: &str) -> Option<f32> {
profile.params.get(key).copied()
}
#[allow(dead_code)]
pub fn profile_param_count(profile: &FaceProfile) -> usize {
profile.params.len()
}
#[allow(dead_code)]
pub fn profile_to_json(profile: &FaceProfile) -> String {
let mut entries: Vec<String> = profile
.params
.iter()
.map(|(k, v)| format!("\"{}\":{:.4}", k, v))
.collect();
entries.sort();
format!("{{\"name\":\"{}\",\"params\":{{{}}}}}", profile.name, entries.join(","))
}
#[allow(dead_code)]
pub fn profile_blend(a: &FaceProfile, b: &FaceProfile, t: f32) -> FaceProfile {
let t = t.clamp(0.0, 1.0);
let mut result = new_face_profile(&a.name);
for (k, va) in &a.params {
let vb = b.params.get(k).copied().unwrap_or(0.0);
result.params.insert(k.clone(), va + (vb - va) * t);
}
for (k, vb) in &b.params {
if !a.params.contains_key(k) {
result.params.insert(k.clone(), vb * t);
}
}
result
}
#[allow(dead_code)]
pub fn profile_clear(profile: &mut FaceProfile) {
profile.params.clear();
}
#[allow(dead_code)]
pub fn profile_name_fp(profile: &FaceProfile) -> &str {
&profile.name
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_profile_empty() {
let p = new_face_profile("base");
assert_eq!(profile_param_count(&p), 0);
}
#[test]
fn set_and_get_param() {
let mut p = new_face_profile("p");
profile_set_param(&mut p, "jaw_width", 0.5);
assert!((profile_get_param(&p, "jaw_width").expect("should succeed") - 0.5).abs() < 1e-6);
}
#[test]
fn get_missing_param() {
let p = new_face_profile("p");
assert!(profile_get_param(&p, "nope").is_none());
}
#[test]
fn param_count() {
let mut p = new_face_profile("p");
profile_set_param(&mut p, "a", 1.0);
profile_set_param(&mut p, "b", 2.0);
assert_eq!(profile_param_count(&p), 2);
}
#[test]
fn blend_half() {
let mut a = new_face_profile("a");
profile_set_param(&mut a, "x", 0.0);
let mut b = new_face_profile("b");
profile_set_param(&mut b, "x", 1.0);
let c = profile_blend(&a, &b, 0.5);
assert!((profile_get_param(&c, "x").expect("should succeed") - 0.5).abs() < 1e-6);
}
#[test]
fn clear_params() {
let mut p = new_face_profile("p");
profile_set_param(&mut p, "x", 1.0);
profile_clear(&mut p);
assert_eq!(profile_param_count(&p), 0);
}
#[test]
fn name_accessor() {
let p = new_face_profile("myface");
assert_eq!(profile_name_fp(&p), "myface");
}
#[test]
fn to_json() {
let mut p = new_face_profile("test");
profile_set_param(&mut p, "x", 0.5);
let j = profile_to_json(&p);
assert!(j.contains("\"name\":\"test\""));
}
#[test]
fn blend_at_zero() {
let mut a = new_face_profile("a");
profile_set_param(&mut a, "x", 0.2);
let mut b = new_face_profile("b");
profile_set_param(&mut b, "x", 0.8);
let c = profile_blend(&a, &b, 0.0);
assert!((profile_get_param(&c, "x").expect("should succeed") - 0.2).abs() < 1e-6);
}
#[test]
fn blend_at_one() {
let mut a = new_face_profile("a");
profile_set_param(&mut a, "x", 0.2);
let mut b = new_face_profile("b");
profile_set_param(&mut b, "x", 0.8);
let c = profile_blend(&a, &b, 1.0);
assert!((profile_get_param(&c, "x").expect("should succeed") - 0.8).abs() < 1e-6);
}
}