1#[allow(dead_code)]
4pub struct ActionUnit {
5 pub au_code: u32,
6 pub name: String,
7 pub intensity: f32,
8 pub morph_targets: Vec<(String, f32)>,
9 pub bilateral: bool,
10}
11
12#[allow(dead_code)]
13pub struct AuSet {
14 pub units: Vec<ActionUnit>,
15 pub name: String,
16}
17
18#[allow(dead_code)]
19pub struct AuFrame {
20 pub time: f32,
21 pub au_intensities: Vec<(u32, f32)>,
22}
23
24#[allow(dead_code)]
25pub fn standard_facs_set() -> AuSet {
26 let mut set = new_au_set("FACS Standard");
27 let au_defs: &[(u32, &str, bool)] = &[
28 (1, "Inner Brow Raise", true),
29 (2, "Outer Brow Raise", true),
30 (4, "Brow Lowerer", true),
31 (5, "Upper Lid Raiser", true),
32 (6, "Cheek Raiser", true),
33 (7, "Lid Tightener", true),
34 (9, "Nose Wrinkler", true),
35 (10, "Upper Lip Raiser", false),
36 (12, "Lip Corner Puller", true),
37 (15, "Lip Corner Depressor", true),
38 (17, "Chin Raiser", false),
39 (20, "Lip Stretcher", true),
40 (23, "Lip Tightener", false),
41 (25, "Lips Part", false),
42 (26, "Jaw Drop", false),
43 ];
44 for &(code, name, bilateral) in au_defs {
45 let targets = vec![(format!("au_{code}"), 1.0_f32)];
46 add_action_unit(
47 &mut set,
48 ActionUnit {
49 au_code: code,
50 name: name.to_string(),
51 intensity: 0.0,
52 morph_targets: targets,
53 bilateral,
54 },
55 );
56 }
57 set
58}
59
60#[allow(dead_code)]
61pub fn new_au_set(name: &str) -> AuSet {
62 AuSet {
63 units: Vec::new(),
64 name: name.to_string(),
65 }
66}
67
68#[allow(dead_code)]
69pub fn add_action_unit(set: &mut AuSet, au: ActionUnit) {
70 set.units.push(au);
71}
72
73#[allow(dead_code)]
74pub fn get_au(set: &AuSet, code: u32) -> Option<&ActionUnit> {
75 set.units.iter().find(|u| u.au_code == code)
76}
77
78#[allow(dead_code)]
79pub fn set_au_intensity(set: &mut AuSet, code: u32, intensity: f32) -> bool {
80 let clamped = intensity.clamp(0.0, 1.0);
81 if let Some(au) = set.units.iter_mut().find(|u| u.au_code == code) {
82 au.intensity = clamped;
83 true
84 } else {
85 false
86 }
87}
88
89#[allow(dead_code)]
90pub fn evaluate_au_set(set: &AuSet) -> Vec<(String, f32)> {
91 let mut contributions: std::collections::HashMap<String, f32> =
92 std::collections::HashMap::new();
93 for au in &set.units {
94 if au.intensity <= 0.0 {
95 continue;
96 }
97 for (target_name, weight_mult) in &au.morph_targets {
98 let entry = contributions.entry(target_name.clone()).or_insert(0.0);
99 *entry += au.intensity * weight_mult;
100 }
101 }
102 let mut result: Vec<(String, f32)> = contributions.into_iter().collect();
103 result.sort_by(|a, b| a.0.cmp(&b.0));
104 for (_, v) in &mut result {
105 *v = v.clamp(0.0, 1.0);
106 }
107 result
108}
109
110#[allow(dead_code)]
111pub fn au_to_emotion(set: &AuSet) -> &'static str {
112 let active: Vec<u32> = set
113 .units
114 .iter()
115 .filter(|u| u.intensity > 0.5)
116 .map(|u| u.au_code)
117 .collect();
118
119 let has = |code: u32| active.contains(&code);
120
121 if has(1) && has(4) && has(15) {
122 "sadness"
123 } else if has(2) && has(5) && has(20) && has(26) {
124 "fear"
125 } else if has(4) && has(5) && has(23) && has(25) {
126 "anger"
127 } else if has(1) && has(2) && has(5) && has(26) {
128 "surprise"
129 } else if has(9) && has(15) && has(16) {
130 "disgust"
131 } else if has(6) && has(12) {
132 "happiness"
133 } else {
134 "neutral"
135 }
136}
137
138#[allow(dead_code)]
139pub fn au_count(set: &AuSet) -> usize {
140 set.units.len()
141}
142
143#[allow(dead_code)]
144pub fn active_aus(set: &AuSet) -> Vec<&ActionUnit> {
145 set.units.iter().filter(|u| u.intensity > 0.01).collect()
146}
147
148#[allow(dead_code)]
149pub fn reset_all_aus(set: &mut AuSet) {
150 for au in &mut set.units {
151 au.intensity = 0.0;
152 }
153}
154
155#[allow(dead_code)]
156pub fn blend_au_frames(a: &AuFrame, b: &AuFrame, t: f32) -> AuFrame {
157 let t = t.clamp(0.0, 1.0);
158 let mut map: std::collections::HashMap<u32, f32> = std::collections::HashMap::new();
159 for &(code, intensity) in &a.au_intensities {
160 map.insert(code, intensity * (1.0 - t));
161 }
162 for &(code, intensity) in &b.au_intensities {
163 let entry = map.entry(code).or_insert(0.0);
164 *entry += intensity * t;
165 }
166 let mut intensities: Vec<(u32, f32)> = map.into_iter().collect();
167 intensities.sort_by_key(|&(code, _)| code);
168 AuFrame {
169 time: a.time * (1.0 - t) + b.time * t,
170 au_intensities: intensities,
171 }
172}
173
174#[allow(dead_code)]
175pub fn au_frame_from_set(set: &AuSet, time: f32) -> AuFrame {
176 let au_intensities = set.units.iter().map(|u| (u.au_code, u.intensity)).collect();
177 AuFrame {
178 time,
179 au_intensities,
180 }
181}
182
183#[allow(dead_code)]
184pub fn apply_au_frame(set: &mut AuSet, frame: &AuFrame) {
185 for &(code, intensity) in &frame.au_intensities {
186 set_au_intensity(set, code, intensity);
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193
194 #[test]
195 fn test_standard_facs_set_has_15_aus() {
196 let set = standard_facs_set();
197 assert_eq!(au_count(&set), 15);
198 }
199
200 #[test]
201 fn test_new_au_set() {
202 let set = new_au_set("Test Set");
203 assert_eq!(set.name, "Test Set");
204 assert_eq!(au_count(&set), 0);
205 }
206
207 #[test]
208 fn test_add_action_unit() {
209 let mut set = new_au_set("Test");
210 add_action_unit(
211 &mut set,
212 ActionUnit {
213 au_code: 1,
214 name: "Inner Brow Raise".to_string(),
215 intensity: 0.0,
216 morph_targets: vec![("brow_raise".to_string(), 1.0)],
217 bilateral: true,
218 },
219 );
220 assert_eq!(au_count(&set), 1);
221 }
222
223 #[test]
224 fn test_get_au() {
225 let set = standard_facs_set();
226 let au = get_au(&set, 12);
227 assert!(au.is_some());
228 assert_eq!(au.expect("should succeed").au_code, 12);
229 }
230
231 #[test]
232 fn test_get_au_not_found() {
233 let set = standard_facs_set();
234 assert!(get_au(&set, 99).is_none());
235 }
236
237 #[test]
238 fn test_set_au_intensity() {
239 let mut set = standard_facs_set();
240 let result = set_au_intensity(&mut set, 12, 0.8);
241 assert!(result);
242 let au = get_au(&set, 12).expect("should succeed");
243 assert!((au.intensity - 0.8).abs() < 1e-6);
244 }
245
246 #[test]
247 fn test_set_au_intensity_clamps() {
248 let mut set = standard_facs_set();
249 set_au_intensity(&mut set, 1, 2.0);
250 assert!((get_au(&set, 1).expect("should succeed").intensity - 1.0).abs() < 1e-6);
251 }
252
253 #[test]
254 fn test_set_au_intensity_not_found() {
255 let mut set = standard_facs_set();
256 assert!(!set_au_intensity(&mut set, 99, 0.5));
257 }
258
259 #[test]
260 fn test_evaluate_au_set() {
261 let mut set = standard_facs_set();
262 set_au_intensity(&mut set, 12, 0.5);
263 let result = evaluate_au_set(&set);
264 assert!(!result.is_empty());
265 let au12 = result.iter().find(|(name, _)| name == "au_12");
266 assert!(au12.is_some());
267 assert!((au12.expect("should succeed").1 - 0.5).abs() < 1e-6);
268 }
269
270 #[test]
271 fn test_au_to_emotion_happiness() {
272 let mut set = standard_facs_set();
273 set_au_intensity(&mut set, 6, 1.0);
274 set_au_intensity(&mut set, 12, 1.0);
275 assert_eq!(au_to_emotion(&set), "happiness");
276 }
277
278 #[test]
279 fn test_au_to_emotion_neutral() {
280 let set = standard_facs_set();
281 assert_eq!(au_to_emotion(&set), "neutral");
282 }
283
284 #[test]
285 fn test_active_aus() {
286 let mut set = standard_facs_set();
287 set_au_intensity(&mut set, 1, 0.5);
288 set_au_intensity(&mut set, 12, 0.0);
289 let active = active_aus(&set);
290 assert_eq!(active.len(), 1);
291 assert_eq!(active[0].au_code, 1);
292 }
293
294 #[test]
295 fn test_reset_all_aus() {
296 let mut set = standard_facs_set();
297 set_au_intensity(&mut set, 1, 0.8);
298 set_au_intensity(&mut set, 12, 0.6);
299 reset_all_aus(&mut set);
300 assert!(active_aus(&set).is_empty());
301 }
302
303 #[test]
304 fn test_au_frame_from_set() {
305 let mut set = standard_facs_set();
306 set_au_intensity(&mut set, 12, 0.7);
307 let frame = au_frame_from_set(&set, 1.5);
308 assert!((frame.time - 1.5).abs() < 1e-6);
309 let au12_entry = frame.au_intensities.iter().find(|&&(code, _)| code == 12);
310 assert!(au12_entry.is_some());
311 assert!((au12_entry.expect("should succeed").1 - 0.7).abs() < 1e-6);
312 }
313
314 #[test]
315 fn test_apply_au_frame() {
316 let mut set = standard_facs_set();
317 set_au_intensity(&mut set, 12, 0.7);
318 let frame = au_frame_from_set(&set, 0.0);
319 let mut set2 = standard_facs_set();
320 apply_au_frame(&mut set2, &frame);
321 assert!((get_au(&set2, 12).expect("should succeed").intensity - 0.7).abs() < 1e-6);
322 }
323
324 #[test]
325 fn test_blend_au_frames() {
326 let mut set_a = standard_facs_set();
327 set_au_intensity(&mut set_a, 12, 0.0);
328 let frame_a = au_frame_from_set(&set_a, 0.0);
329
330 let mut set_b = standard_facs_set();
331 set_au_intensity(&mut set_b, 12, 1.0);
332 let frame_b = au_frame_from_set(&set_b, 1.0);
333
334 let blended = blend_au_frames(&frame_a, &frame_b, 0.5);
335 let au12 = blended.au_intensities.iter().find(|&&(code, _)| code == 12);
336 assert!(au12.is_some());
337 assert!((au12.expect("should succeed").1 - 0.5).abs() < 1e-6);
338 assert!((blended.time - 0.5).abs() < 1e-6);
339 }
340
341 #[test]
342 fn test_au_count() {
343 let set = standard_facs_set();
344 assert_eq!(au_count(&set), 15);
345 }
346}