#![allow(dead_code)]
#[allow(dead_code)]
pub struct InbetweenKey {
pub name: String,
pub target_weight: f32,
pub deltas: Vec<[f32; 3]>,
}
#[allow(dead_code)]
pub struct BlendShapeInbetweenExport {
pub base_name: String,
pub keys: Vec<InbetweenKey>,
}
#[allow(dead_code)]
pub fn new_bsi_export(base_name: &str) -> BlendShapeInbetweenExport {
BlendShapeInbetweenExport {
base_name: base_name.to_string(),
keys: Vec::new(),
}
}
#[allow(dead_code)]
pub fn add_inbetween_key(
export: &mut BlendShapeInbetweenExport,
name: &str,
target_weight: f32,
deltas: Vec<[f32; 3]>,
) {
export.keys.push(InbetweenKey {
name: name.to_string(),
target_weight,
deltas,
});
}
#[allow(dead_code)]
pub fn inbetween_key_count(export: &BlendShapeInbetweenExport) -> usize {
export.keys.len()
}
#[allow(dead_code)]
pub fn total_inbetween_deltas(export: &BlendShapeInbetweenExport) -> usize {
export.keys.iter().map(|k| k.deltas.len()).sum()
}
#[allow(dead_code)]
pub fn find_inbetween<'a>(
export: &'a BlendShapeInbetweenExport,
name: &str,
) -> Option<&'a InbetweenKey> {
export.keys.iter().find(|k| k.name == name)
}
#[allow(dead_code)]
pub fn max_delta_magnitude_bsi(key: &InbetweenKey) -> f32 {
key.deltas
.iter()
.map(|d| (d[0] * d[0] + d[1] * d[1] + d[2] * d[2]).sqrt())
.fold(0.0_f32, f32::max)
}
#[allow(dead_code)]
pub fn keys_sorted_by_weight(export: &BlendShapeInbetweenExport) -> bool {
export
.keys
.windows(2)
.all(|w| w[0].target_weight <= w[1].target_weight)
}
#[allow(dead_code)]
pub fn interpolate_inbetween(k0: &InbetweenKey, k1: &InbetweenKey, t: f32) -> Vec<[f32; 3]> {
let n = k0.deltas.len().min(k1.deltas.len());
(0..n)
.map(|i| {
let d0 = k0.deltas[i];
let d1 = k1.deltas[i];
[
d0[0] + t * (d1[0] - d0[0]),
d0[1] + t * (d1[1] - d0[1]),
d0[2] + t * (d1[2] - d0[2]),
]
})
.collect()
}
#[allow(dead_code)]
pub fn bsi_to_json(export: &BlendShapeInbetweenExport) -> String {
format!(
r#"{{"base":"{}","keys":{}}}"#,
export.base_name,
export.keys.len()
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create_and_add() {
let mut e = new_bsi_export("mouth_open");
add_inbetween_key(&mut e, "half", 0.5, vec![[0.0, 0.1, 0.0]; 4]);
assert_eq!(inbetween_key_count(&e), 1);
}
#[test]
fn total_deltas() {
let mut e = new_bsi_export("eye");
add_inbetween_key(&mut e, "k1", 0.5, vec![[0.0; 3]; 3]);
add_inbetween_key(&mut e, "k2", 1.0, vec![[0.0; 3]; 3]);
assert_eq!(total_inbetween_deltas(&e), 6);
}
#[test]
fn find_key() {
let mut e = new_bsi_export("brow");
add_inbetween_key(&mut e, "mid", 0.5, vec![]);
assert!(find_inbetween(&e, "mid").is_some());
}
#[test]
fn max_delta_mag() {
let k = InbetweenKey {
name: "k".to_string(),
target_weight: 1.0,
deltas: vec![[3.0, 4.0, 0.0]],
};
assert!((max_delta_magnitude_bsi(&k) - 5.0).abs() < 1e-5);
}
#[test]
fn sorted_by_weight() {
let mut e = new_bsi_export("test");
add_inbetween_key(&mut e, "a", 0.25, vec![]);
add_inbetween_key(&mut e, "b", 0.75, vec![]);
assert!(keys_sorted_by_weight(&e));
}
#[test]
fn not_sorted() {
let mut e = new_bsi_export("test");
add_inbetween_key(&mut e, "a", 0.75, vec![]);
add_inbetween_key(&mut e, "b", 0.25, vec![]);
assert!(!keys_sorted_by_weight(&e));
}
#[test]
fn interpolate_midpoint() {
let k0 = InbetweenKey {
name: "k0".to_string(),
target_weight: 0.0,
deltas: vec![[0.0; 3]],
};
let k1 = InbetweenKey {
name: "k1".to_string(),
target_weight: 1.0,
deltas: vec![[2.0, 0.0, 0.0]],
};
let mid = interpolate_inbetween(&k0, &k1, 0.5);
assert!((mid[0][0] - 1.0).abs() < 1e-5);
}
#[test]
fn json_has_base() {
let e = new_bsi_export("cheek");
let j = bsi_to_json(&e);
assert!(j.contains("\"base\":\"cheek\""));
}
#[test]
fn empty_keys() {
let e = new_bsi_export("x");
assert_eq!(inbetween_key_count(&e), 0);
}
#[test]
fn find_missing() {
let e = new_bsi_export("x");
assert!(find_inbetween(&e, "y").is_none());
}
}