#![allow(dead_code)]
#[derive(Debug, Clone)]
pub struct ShapeKey {
pub name: String,
pub weight: f32,
pub positions: Vec<[f32; 3]>,
}
impl ShapeKey {
pub fn new(name: &str, weight: f32, positions: Vec<[f32; 3]>) -> Self {
Self {
name: name.to_string(),
weight: weight.clamp(0.0, 1.0),
positions,
}
}
}
pub fn mix_shape_keys(basis: &[[f32; 3]], keys: &[ShapeKey]) -> Vec<[f32; 3]> {
let n = basis.len();
let mut result = basis.to_vec();
for key in keys {
let w = key.weight;
let plen = key.positions.len().min(n);
for i in 0..plen {
result[i][0] += (key.positions[i][0] - basis[i][0]) * w;
result[i][1] += (key.positions[i][1] - basis[i][1]) * w;
result[i][2] += (key.positions[i][2] - basis[i][2]) * w;
}
}
result
}
pub fn set_key_weight(keys: &mut [ShapeKey], name: &str, weight: f32) -> bool {
if let Some(k) = keys.iter_mut().find(|k| k.name == name) {
k.weight = weight.clamp(0.0, 1.0);
true
} else {
false
}
}
pub fn total_influence(keys: &[ShapeKey]) -> f32 {
keys.iter().map(|k| k.weight).sum()
}
pub fn dominant_key(keys: &[ShapeKey]) -> Option<usize> {
keys.iter()
.enumerate()
.max_by(|a, b| {
a.1.weight
.partial_cmp(&b.1.weight)
.unwrap_or(std::cmp::Ordering::Equal)
})
.map(|(i, _)| i)
}
pub fn reset_all_weights(keys: &mut [ShapeKey]) {
for k in keys.iter_mut() {
k.weight = 0.0;
}
}
#[cfg(test)]
mod tests {
use super::*;
fn basis() -> Vec<[f32; 3]> {
vec![[0.0_f32, 0.0, 0.0], [1.0, 0.0, 0.0]]
}
fn two_keys() -> Vec<ShapeKey> {
vec![
ShapeKey::new("smile", 0.5, vec![[0.0, 0.5, 0.0], [1.0, 0.5, 0.0]]),
ShapeKey::new("open", 0.0, vec![[0.0, 1.0, 0.0], [1.0, 1.0, 0.0]]),
]
}
#[test]
fn test_mix_basis_only() {
let b = basis();
let keys = vec![ShapeKey::new("k", 0.0, b.clone())];
let result = mix_shape_keys(&b, &keys);
assert!((result[0][0] - 0.0).abs() < 1e-6);
}
#[test]
fn test_mix_full_weight() {
let b = basis();
let target = vec![[0.0_f32, 2.0, 0.0], [1.0, 2.0, 0.0]];
let keys = vec![ShapeKey::new("k", 1.0, target.clone())];
let result = mix_shape_keys(&b, &keys);
assert!((result[0][1] - 2.0).abs() < 1e-5);
}
#[test]
fn test_mix_half_weight() {
let b = basis();
let target = vec![[0.0_f32, 2.0, 0.0], [1.0, 2.0, 0.0]];
let keys = vec![ShapeKey::new("k", 0.5, target)];
let result = mix_shape_keys(&b, &keys);
assert!((result[0][1] - 1.0).abs() < 1e-5);
}
#[test]
fn test_set_key_weight() {
let mut keys = two_keys();
assert!(set_key_weight(&mut keys, "smile", 0.8));
assert!((keys[0].weight - 0.8).abs() < 1e-6);
}
#[test]
fn test_set_key_weight_missing() {
let mut keys = two_keys();
assert!(!set_key_weight(&mut keys, "no_such_key", 0.5));
}
#[test]
fn test_total_influence() {
let keys = two_keys();
assert!((total_influence(&keys) - 0.5).abs() < 1e-6);
}
#[test]
fn test_dominant_key() {
let keys = two_keys();
assert_eq!(dominant_key(&keys), Some(0));
}
#[test]
fn test_reset_all_weights() {
let mut keys = two_keys();
reset_all_weights(&mut keys);
assert!((total_influence(&keys) - 0.0).abs() < 1e-6);
}
}