#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct EmotionCurve {
pub name: String,
pub samples: Vec<f32>,
pub duration: f32,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct EmotionBlendSet {
pub curves: Vec<EmotionCurve>,
}
#[allow(dead_code)]
pub fn new_emotion_blend_set() -> EmotionBlendSet {
EmotionBlendSet { curves: Vec::new() }
}
#[allow(dead_code)]
pub fn add_emotion_curve(set: &mut EmotionBlendSet, name: &str, samples: Vec<f32>, duration: f32) {
set.curves.push(EmotionCurve { name: name.to_owned(), samples, duration });
}
#[allow(dead_code)]
pub fn evaluate_emotion(set: &EmotionBlendSet, name: &str, t: f32) -> f32 {
for c in &set.curves {
if c.name == name {
return sample_curve(c, t);
}
}
0.0
}
fn sample_curve(c: &EmotionCurve, t: f32) -> f32 {
if c.samples.is_empty() {
return 0.0;
}
let n = c.samples.len();
if n == 1 {
return c.samples[0];
}
let t_clamped = t.clamp(0.0, c.duration);
let frac = t_clamped / c.duration.max(f32::EPSILON) * (n - 1) as f32;
let lo = frac.floor() as usize;
let hi = (lo + 1).min(n - 1);
let alpha = frac - lo as f32;
c.samples[lo] * (1.0 - alpha) + c.samples[hi] * alpha
}
#[allow(dead_code)]
pub fn blend_two_emotions(set: &EmotionBlendSet, name_a: &str, name_b: &str, t: f32, at: f32) -> f32 {
let a = evaluate_emotion(set, name_a, at);
let b = evaluate_emotion(set, name_b, at);
a * (1.0 - t) + b * t
}
#[allow(dead_code)]
pub fn emotion_count(set: &EmotionBlendSet) -> usize {
set.curves.len()
}
#[allow(dead_code)]
pub fn emotion_name(set: &EmotionBlendSet, index: usize) -> Option<&str> {
set.curves.get(index).map(|c| c.name.as_str())
}
#[allow(dead_code)]
pub fn emotion_at_time(set: &EmotionBlendSet, name: &str, t: f32) -> f32 {
evaluate_emotion(set, name, t)
}
#[allow(dead_code)]
pub fn emotion_peak_time(set: &EmotionBlendSet, name: &str) -> f32 {
for c in &set.curves {
if c.name == name && !c.samples.is_empty() {
let (idx, _) = c
.samples
.iter()
.enumerate()
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
.unwrap_or((0, &0.0));
let n = c.samples.len();
return idx as f32 / (n - 1).max(1) as f32 * c.duration;
}
}
0.0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_empty_set() {
let s = new_emotion_blend_set();
assert_eq!(emotion_count(&s), 0);
}
#[test]
fn test_add_emotion_curve() {
let mut s = new_emotion_blend_set();
add_emotion_curve(&mut s, "happy", vec![0.0, 1.0], 1.0);
assert_eq!(emotion_count(&s), 1);
}
#[test]
fn test_emotion_name() {
let mut s = new_emotion_blend_set();
add_emotion_curve(&mut s, "sad", vec![0.5], 1.0);
assert_eq!(emotion_name(&s, 0), Some("sad"));
assert!(emotion_name(&s, 1).is_none());
}
#[test]
fn test_evaluate_emotion_start() {
let mut s = new_emotion_blend_set();
add_emotion_curve(&mut s, "joy", vec![0.0, 1.0], 1.0);
let v = evaluate_emotion(&s, "joy", 0.0);
assert!((v).abs() < 1e-5);
}
#[test]
fn test_evaluate_emotion_end() {
let mut s = new_emotion_blend_set();
add_emotion_curve(&mut s, "joy", vec![0.0, 1.0], 1.0);
let v = evaluate_emotion(&s, "joy", 1.0);
assert!((v - 1.0).abs() < 1e-5);
}
#[test]
fn test_evaluate_unknown_emotion() {
let s = new_emotion_blend_set();
assert_eq!(evaluate_emotion(&s, "unknown", 0.5), 0.0);
}
#[test]
fn test_blend_two_emotions_midpoint() {
let mut s = new_emotion_blend_set();
add_emotion_curve(&mut s, "a", vec![0.0, 0.0], 1.0);
add_emotion_curve(&mut s, "b", vec![1.0, 1.0], 1.0);
let v = blend_two_emotions(&s, "a", "b", 0.5, 0.5);
assert!((v - 0.5).abs() < 1e-5);
}
#[test]
fn test_emotion_peak_time() {
let mut s = new_emotion_blend_set();
add_emotion_curve(&mut s, "anger", vec![0.0, 0.5, 1.0, 0.5], 3.0);
let t = emotion_peak_time(&s, "anger");
assert!((t - 2.0).abs() < 1e-4);
}
#[test]
fn test_emotion_at_time_midpoint() {
let mut s = new_emotion_blend_set();
add_emotion_curve(&mut s, "x", vec![0.0, 1.0], 2.0);
let v = emotion_at_time(&s, "x", 1.0);
assert!((v - 0.5).abs() < 1e-4);
}
}