#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct ShapeDriverConfig {
pub max_keys: usize,
pub enabled: bool,
}
#[allow(dead_code)]
impl ShapeDriverConfig {
fn new() -> Self {
Self {
max_keys: 64,
enabled: true,
}
}
}
#[allow(dead_code)]
pub fn default_shape_driver_config() -> ShapeDriverConfig {
ShapeDriverConfig::new()
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct ShapeKey {
pub param: f32,
pub weight: f32,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct ShapeDriver {
config: ShapeDriverConfig,
keys: Vec<ShapeKey>,
current_param: f32,
}
#[allow(dead_code)]
pub fn new_shape_driver(config: ShapeDriverConfig) -> ShapeDriver {
ShapeDriver {
config,
keys: Vec::new(),
current_param: 0.0,
}
}
#[allow(dead_code)]
pub fn shape_driver_add_key(driver: &mut ShapeDriver, param: f32, weight: f32) -> bool {
if driver.keys.len() >= driver.config.max_keys {
return false;
}
let p = param.clamp(0.0, 1.0);
let w = weight.clamp(0.0, 1.0);
for k in &mut driver.keys {
if (k.param - p).abs() < f32::EPSILON {
k.weight = w;
return true;
}
}
driver.keys.push(ShapeKey { param: p, weight: w });
driver.keys.sort_by(|a, b| a.param.partial_cmp(&b.param).unwrap_or(std::cmp::Ordering::Equal));
true
}
#[allow(dead_code)]
pub fn shape_driver_evaluate(driver: &ShapeDriver, param: f32) -> f32 {
if !driver.config.enabled || driver.keys.is_empty() {
return 0.0;
}
let p = param.clamp(0.0, 1.0);
if let Some(first) = driver.keys.first() {
if p <= first.param {
return first.weight;
}
}
if let Some(last) = driver.keys.last() {
if p >= last.param {
return last.weight;
}
}
for i in 0..driver.keys.len() - 1 {
let lo = &driver.keys[i];
let hi = &driver.keys[i + 1];
if p >= lo.param && p <= hi.param {
let t = (p - lo.param) / (hi.param - lo.param);
return lo.weight + t * (hi.weight - lo.weight);
}
}
0.0
}
#[allow(dead_code)]
pub fn shape_driver_key_count(driver: &ShapeDriver) -> usize {
driver.keys.len()
}
#[allow(dead_code)]
pub fn shape_driver_set_param(driver: &mut ShapeDriver, param: f32) {
driver.current_param = param.clamp(0.0, 1.0);
}
#[allow(dead_code)]
pub fn shape_driver_current_weight(driver: &ShapeDriver) -> f32 {
shape_driver_evaluate(driver, driver.current_param)
}
#[allow(dead_code)]
pub fn shape_driver_to_json(driver: &ShapeDriver) -> String {
let keys: Vec<String> = driver
.keys
.iter()
.map(|k| format!("{{\"param\":{:.4},\"weight\":{:.4}}}", k.param, k.weight))
.collect();
format!(
"{{\"enabled\":{},\"current_param\":{:.4},\"keys\":[{}]}}",
driver.config.enabled,
driver.current_param,
keys.join(",")
)
}
#[allow(dead_code)]
pub fn shape_driver_clear(driver: &mut ShapeDriver) {
driver.keys.clear();
driver.current_param = 0.0;
}
#[allow(dead_code)]
pub fn shape_driver_is_active(driver: &ShapeDriver) -> bool {
driver.config.enabled && !driver.keys.is_empty()
}
#[cfg(test)]
mod tests {
use super::*;
fn make_driver() -> ShapeDriver {
let cfg = default_shape_driver_config();
let mut d = new_shape_driver(cfg);
shape_driver_add_key(&mut d, 0.0, 0.0);
shape_driver_add_key(&mut d, 0.5, 0.5);
shape_driver_add_key(&mut d, 1.0, 1.0);
d
}
#[test]
fn test_key_count() {
let d = make_driver();
assert_eq!(shape_driver_key_count(&d), 3);
}
#[test]
fn test_evaluate_midpoint() {
let d = make_driver();
let w = shape_driver_evaluate(&d, 0.25);
assert!((w - 0.25).abs() < 1e-4);
}
#[test]
fn test_evaluate_at_key() {
let d = make_driver();
assert!((shape_driver_evaluate(&d, 0.5) - 0.5).abs() < 1e-4);
}
#[test]
fn test_evaluate_below_range() {
let d = make_driver();
assert!((shape_driver_evaluate(&d, -0.5) - 0.0).abs() < 1e-4);
}
#[test]
fn test_evaluate_above_range() {
let d = make_driver();
assert!((shape_driver_evaluate(&d, 1.5) - 1.0).abs() < 1e-4);
}
#[test]
fn test_set_param_and_current_weight() {
let mut d = make_driver();
shape_driver_set_param(&mut d, 0.75);
let w = shape_driver_current_weight(&d);
assert!((w - 0.75).abs() < 1e-4);
}
#[test]
fn test_clear() {
let mut d = make_driver();
shape_driver_clear(&mut d);
assert_eq!(shape_driver_key_count(&d), 0);
assert!(!shape_driver_is_active(&d));
}
#[test]
fn test_is_active() {
let d = make_driver();
assert!(shape_driver_is_active(&d));
}
#[test]
fn test_to_json_contains_keys() {
let d = make_driver();
let json = shape_driver_to_json(&d);
assert!(json.contains("keys"));
assert!(json.contains("enabled"));
}
#[test]
fn test_max_keys_limit() {
let cfg = ShapeDriverConfig {
max_keys: 2,
enabled: true,
};
let mut d = new_shape_driver(cfg);
assert!(shape_driver_add_key(&mut d, 0.0, 0.0));
assert!(shape_driver_add_key(&mut d, 1.0, 1.0));
assert!(!shape_driver_add_key(&mut d, 0.5, 0.5));
}
}