oxihuman_morph/
emotion_blend_tree.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone, Copy, PartialEq)]
9pub enum BlendOp {
10 Add,
11 Multiply,
12 Override,
13}
14
15#[derive(Debug, Clone)]
17pub struct EmotionNode {
18 pub name: String,
19 pub weight: f32,
20 pub children: Vec<usize>,
21 pub blend_op: BlendOp,
22}
23
24#[derive(Debug, Clone)]
26pub struct EmotionBlendTree {
27 pub nodes: Vec<EmotionNode>,
28 pub root: Option<usize>,
29 pub output_dim: usize,
30 pub enabled: bool,
31}
32
33impl EmotionBlendTree {
34 pub fn new(output_dim: usize) -> Self {
35 EmotionBlendTree {
36 nodes: Vec::new(),
37 root: None,
38 output_dim,
39 enabled: true,
40 }
41 }
42}
43
44pub fn new_emotion_blend_tree(output_dim: usize) -> EmotionBlendTree {
46 EmotionBlendTree::new(output_dim)
47}
48
49pub fn ebt_add_node(tree: &mut EmotionBlendTree, node: EmotionNode) -> usize {
51 let idx = tree.nodes.len();
52 tree.nodes.push(node);
53 idx
54}
55
56pub fn ebt_set_root(tree: &mut EmotionBlendTree, root: usize) {
58 tree.root = Some(root);
59}
60
61pub fn ebt_evaluate(tree: &EmotionBlendTree) -> Vec<f32> {
63 vec![0.0; tree.output_dim]
65}
66
67pub fn ebt_node_count(tree: &EmotionBlendTree) -> usize {
69 tree.nodes.len()
70}
71
72pub fn ebt_set_enabled(tree: &mut EmotionBlendTree, enabled: bool) {
74 tree.enabled = enabled;
75}
76
77pub fn ebt_to_json(tree: &EmotionBlendTree) -> String {
79 format!(
80 r#"{{"node_count":{},"output_dim":{},"has_root":{},"enabled":{}}}"#,
81 tree.nodes.len(),
82 tree.output_dim,
83 tree.root.is_some(),
84 tree.enabled
85 )
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn test_new_output_dim() {
94 let t = new_emotion_blend_tree(10);
95 assert_eq!(t.output_dim, 10 ,);
96 }
97
98 #[test]
99 fn test_no_nodes_initially() {
100 let t = new_emotion_blend_tree(5);
101 assert_eq!(ebt_node_count(&t), 0 ,);
102 }
103
104 #[test]
105 fn test_add_node_returns_index() {
106 let mut t = new_emotion_blend_tree(5);
107 let idx = ebt_add_node(
108 &mut t,
109 EmotionNode {
110 name: "joy".into(),
111 weight: 1.0,
112 children: vec![],
113 blend_op: BlendOp::Add,
114 },
115 );
116 assert_eq!(idx, 0 ,);
117 }
118
119 #[test]
120 fn test_set_root() {
121 let mut t = new_emotion_blend_tree(5);
122 let idx = ebt_add_node(
123 &mut t,
124 EmotionNode {
125 name: "root".into(),
126 weight: 1.0,
127 children: vec![],
128 blend_op: BlendOp::Add,
129 },
130 );
131 ebt_set_root(&mut t, idx);
132 assert_eq!(t.root, Some(0) ,);
133 }
134
135 #[test]
136 fn test_evaluate_output_length() {
137 let t = new_emotion_blend_tree(8);
138 let out = ebt_evaluate(&t);
139 assert_eq!(out.len(), 8 ,);
140 }
141
142 #[test]
143 fn test_evaluate_zeroed() {
144 let t = new_emotion_blend_tree(3);
145 let out = ebt_evaluate(&t);
146 assert!(out.iter().all(|&v| v.abs() < 1e-6), );
147 }
148
149 #[test]
150 fn test_set_enabled() {
151 let mut t = new_emotion_blend_tree(3);
152 ebt_set_enabled(&mut t, false);
153 assert!(!t.enabled ,);
154 }
155
156 #[test]
157 fn test_to_json_contains_node_count() {
158 let t = new_emotion_blend_tree(4);
159 let j = ebt_to_json(&t);
160 assert!(j.contains("\"node_count\""), );
161 }
162
163 #[test]
164 fn test_enabled_default() {
165 let t = new_emotion_blend_tree(1);
166 assert!(t.enabled ,);
167 }
168
169 #[test]
170 fn test_no_root_initially() {
171 let t = new_emotion_blend_tree(2);
172 assert!(t.root.is_none() ,);
173 }
174}