#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct CorrectiveShapeDef {
pub name: String,
pub offsets: Vec<[f32; 3]>,
pub driver_index: usize,
pub threshold: f32,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct CorrectiveShapeSet {
pub shapes: Vec<CorrectiveShapeDef>,
}
#[allow(dead_code)]
pub fn new_corrective_shape_set() -> CorrectiveShapeSet {
CorrectiveShapeSet::default()
}
#[allow(dead_code)]
pub fn add_corrective(
css: &mut CorrectiveShapeSet,
name: &str,
driver: usize,
threshold: f32,
offsets: Vec<[f32; 3]>,
) {
css.shapes.push(CorrectiveShapeDef {
name: name.to_string(),
offsets,
driver_index: driver,
threshold: threshold.clamp(0.0, 1.0),
});
}
#[allow(dead_code)]
pub fn evaluate_correctives(css: &CorrectiveShapeSet, weights: &[f32]) -> Vec<f32> {
let n_verts = css
.shapes
.iter()
.map(|s| s.offsets.len())
.max()
.unwrap_or(0);
let mut result = vec![0.0_f32; n_verts * 3];
for shape in &css.shapes {
let driver_w = weights.get(shape.driver_index).copied().unwrap_or(0.0);
if driver_w < shape.threshold {
continue;
}
let blend = ((driver_w - shape.threshold) / (1.0 - shape.threshold + 1e-9)).clamp(0.0, 1.0);
for (i, off) in shape.offsets.iter().enumerate() {
result[i * 3] += off[0] * blend;
result[i * 3 + 1] += off[1] * blend;
result[i * 3 + 2] += off[2] * blend;
}
}
result
}
#[allow(dead_code)]
pub fn corrective_count(css: &CorrectiveShapeSet) -> usize {
css.shapes.len()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_set_is_empty() {
let css = new_corrective_shape_set();
assert_eq!(corrective_count(&css), 0);
}
#[test]
fn add_corrective_increments_count() {
let mut css = new_corrective_shape_set();
add_corrective(&mut css, "jaw_open", 0, 0.5, vec![[0.1, 0.0, 0.0]]);
assert_eq!(corrective_count(&css), 1);
}
#[test]
fn evaluate_empty_returns_empty() {
let css = new_corrective_shape_set();
let result = evaluate_correctives(&css, &[]);
assert!(result.is_empty());
}
#[test]
fn corrective_not_triggered_below_threshold() {
let mut css = new_corrective_shape_set();
add_corrective(&mut css, "s", 0, 0.8, vec![[1.0, 0.0, 0.0]]);
let result = evaluate_correctives(&css, &[0.5]);
assert!(result.iter().all(|&v| v.abs() < 1e-6));
}
#[test]
fn corrective_triggered_above_threshold() {
let mut css = new_corrective_shape_set();
add_corrective(&mut css, "s", 0, 0.5, vec![[1.0, 0.0, 0.0]]);
let result = evaluate_correctives(&css, &[1.0]);
assert!(result[0] > 0.0);
}
#[test]
fn threshold_clamped_to_range() {
let mut css = new_corrective_shape_set();
add_corrective(&mut css, "s", 0, 2.0, vec![[1.0, 0.0, 0.0]]);
assert!((css.shapes[0].threshold - 1.0).abs() < 1e-6);
}
#[test]
fn driver_index_out_of_range_no_panic() {
let mut css = new_corrective_shape_set();
add_corrective(&mut css, "s", 99, 0.0, vec![[1.0, 0.0, 0.0]]);
let result = evaluate_correctives(&css, &[1.0]);
let _ = result;
}
#[test]
fn two_shapes_sum_offsets() {
let mut css = new_corrective_shape_set();
add_corrective(&mut css, "a", 0, 0.0, vec![[0.3, 0.0, 0.0]]);
add_corrective(&mut css, "b", 0, 0.0, vec![[0.2, 0.0, 0.0]]);
let result = evaluate_correctives(&css, &[1.0]);
assert!(result[0] > 0.4);
}
#[test]
fn corrective_count_multiple() {
let mut css = new_corrective_shape_set();
for i in 0..5usize {
add_corrective(&mut css, "s", i, 0.5, vec![]);
}
assert_eq!(corrective_count(&css), 5);
}
#[test]
fn evaluate_returns_correct_length() {
let mut css = new_corrective_shape_set();
add_corrective(&mut css, "s", 0, 0.0, vec![[0.0; 3]; 4]);
let result = evaluate_correctives(&css, &[1.0]);
assert_eq!(result.len(), 12); }
}