oxihuman_export/
animation_layer_export.rs1#![allow(dead_code)]
4
5#[allow(dead_code)]
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum LayerBlendMode {
11 Override,
12 Additive,
13 Multiply,
14}
15
16#[allow(dead_code)]
18#[derive(Debug, Clone)]
19pub struct AnimLayer {
20 pub name: String,
21 pub weight: f32,
22 pub blend_mode: LayerBlendMode,
23 pub enabled: bool,
24 pub track_count: usize,
25}
26
27#[allow(dead_code)]
29#[derive(Debug, Clone)]
30pub struct AnimLayerExport {
31 pub layers: Vec<AnimLayer>,
32}
33
34#[allow(dead_code)]
36pub fn new_anim_layer_export() -> AnimLayerExport {
37 AnimLayerExport { layers: Vec::new() }
38}
39
40#[allow(dead_code)]
42pub fn add_anim_layer(exp: &mut AnimLayerExport, layer: AnimLayer) {
43 exp.layers.push(layer);
44}
45
46#[allow(dead_code)]
48pub fn anim_layer_count(exp: &AnimLayerExport) -> usize {
49 exp.layers.len()
50}
51
52#[allow(dead_code)]
54pub fn total_enabled_weight(exp: &AnimLayerExport) -> f32 {
55 exp.layers
56 .iter()
57 .filter(|l| l.enabled)
58 .map(|l| l.weight)
59 .sum()
60}
61
62#[allow(dead_code)]
64pub fn find_layer_by_name<'a>(exp: &'a AnimLayerExport, name: &str) -> Option<&'a AnimLayer> {
65 exp.layers.iter().find(|l| l.name == name)
66}
67
68#[allow(dead_code)]
70pub fn enabled_layer_count(exp: &AnimLayerExport) -> usize {
71 exp.layers.iter().filter(|l| l.enabled).count()
72}
73
74#[allow(dead_code)]
76pub fn anim_layer_to_json(exp: &AnimLayerExport) -> String {
77 format!(
78 "{{\"layer_count\":{},\"enabled\":{}}}",
79 anim_layer_count(exp),
80 enabled_layer_count(exp)
81 )
82}
83
84#[allow(dead_code)]
86pub fn blend_mode_name(mode: LayerBlendMode) -> &'static str {
87 match mode {
88 LayerBlendMode::Override => "override",
89 LayerBlendMode::Additive => "additive",
90 LayerBlendMode::Multiply => "multiply",
91 }
92}
93
94#[allow(dead_code)]
96pub fn validate_anim_layers(exp: &AnimLayerExport) -> bool {
97 exp.layers.iter().all(|l| (0.0..=1.0).contains(&l.weight))
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 fn sample_layer(name: &str, w: f32) -> AnimLayer {
105 AnimLayer {
106 name: name.to_string(),
107 weight: w,
108 blend_mode: LayerBlendMode::Override,
109 enabled: true,
110 track_count: 3,
111 }
112 }
113
114 #[test]
115 fn new_export_empty() {
116 let exp = new_anim_layer_export();
117 assert_eq!(anim_layer_count(&exp), 0);
118 }
119
120 #[test]
121 fn add_layer_increments() {
122 let mut exp = new_anim_layer_export();
123 add_anim_layer(&mut exp, sample_layer("base", 1.0));
124 assert_eq!(anim_layer_count(&exp), 1);
125 }
126
127 #[test]
128 fn total_weight_sums() {
129 let mut exp = new_anim_layer_export();
130 add_anim_layer(&mut exp, sample_layer("a", 0.5));
131 add_anim_layer(&mut exp, sample_layer("b", 0.3));
132 assert!((total_enabled_weight(&exp) - 0.8).abs() < 1e-5);
133 }
134
135 #[test]
136 fn find_by_name_some() {
137 let mut exp = new_anim_layer_export();
138 add_anim_layer(&mut exp, sample_layer("walk", 1.0));
139 assert!(find_layer_by_name(&exp, "walk").is_some());
140 }
141
142 #[test]
143 fn find_by_name_none() {
144 let exp = new_anim_layer_export();
145 assert!(find_layer_by_name(&exp, "missing").is_none());
146 }
147
148 #[test]
149 fn enabled_count() {
150 let mut exp = new_anim_layer_export();
151 add_anim_layer(
152 &mut exp,
153 AnimLayer {
154 enabled: false,
155 ..sample_layer("x", 0.5)
156 },
157 );
158 add_anim_layer(&mut exp, sample_layer("y", 0.5));
159 assert_eq!(enabled_layer_count(&exp), 1);
160 }
161
162 #[test]
163 fn validate_valid() {
164 let mut exp = new_anim_layer_export();
165 add_anim_layer(&mut exp, sample_layer("a", 0.7));
166 assert!(validate_anim_layers(&exp));
167 }
168
169 #[test]
170 fn blend_mode_names() {
171 assert_eq!(blend_mode_name(LayerBlendMode::Additive), "additive");
172 }
173
174 #[test]
175 fn json_contains_layer_count() {
176 let mut exp = new_anim_layer_export();
177 add_anim_layer(&mut exp, sample_layer("base", 1.0));
178 let j = anim_layer_to_json(&exp);
179 assert!(j.contains("layer_count"));
180 }
181
182 #[test]
183 fn weight_in_range() {
184 let l = sample_layer("t", 0.5);
185 assert!((0.0..=1.0).contains(&l.weight));
186 }
187}