oxihuman_export/
bone_custom_prop_export.rs1#![allow(dead_code)]
4
5#[allow(dead_code)]
7#[derive(Clone)]
8pub enum BonePropValue {
9 Float(f32),
10 Int(i32),
11 Bool(bool),
12 Text(String),
13}
14
15#[allow(dead_code)]
17pub struct BoneCustomProp {
18 pub bone_name: String,
19 pub key: String,
20 pub value: BonePropValue,
21}
22
23#[allow(dead_code)]
25#[derive(Default)]
26pub struct BoneCustomPropExport {
27 pub props: Vec<BoneCustomProp>,
28}
29
30#[allow(dead_code)]
32pub fn new_bone_custom_prop_export() -> BoneCustomPropExport {
33 BoneCustomPropExport::default()
34}
35
36#[allow(dead_code)]
38pub fn add_float_prop(export: &mut BoneCustomPropExport, bone: &str, key: &str, val: f32) {
39 export.props.push(BoneCustomProp {
40 bone_name: bone.to_string(),
41 key: key.to_string(),
42 value: BonePropValue::Float(val),
43 });
44}
45
46#[allow(dead_code)]
48pub fn add_bool_prop(export: &mut BoneCustomPropExport, bone: &str, key: &str, val: bool) {
49 export.props.push(BoneCustomProp {
50 bone_name: bone.to_string(),
51 key: key.to_string(),
52 value: BonePropValue::Bool(val),
53 });
54}
55
56#[allow(dead_code)]
58pub fn add_text_prop(export: &mut BoneCustomPropExport, bone: &str, key: &str, val: &str) {
59 export.props.push(BoneCustomProp {
60 bone_name: bone.to_string(),
61 key: key.to_string(),
62 value: BonePropValue::Text(val.to_string()),
63 });
64}
65
66#[allow(dead_code)]
68pub fn prop_count(export: &BoneCustomPropExport) -> usize {
69 export.props.len()
70}
71
72#[allow(dead_code)]
74pub fn find_prop<'a>(
75 export: &'a BoneCustomPropExport,
76 bone: &str,
77 key: &str,
78) -> Option<&'a BoneCustomProp> {
79 export
80 .props
81 .iter()
82 .find(|p| p.bone_name == bone && p.key == key)
83}
84
85#[allow(dead_code)]
87pub fn props_for_bone<'a>(export: &'a BoneCustomPropExport, bone: &str) -> Vec<&'a BoneCustomProp> {
88 export
89 .props
90 .iter()
91 .filter(|p| p.bone_name == bone)
92 .collect()
93}
94
95#[allow(dead_code)]
97pub fn bone_names_with_props(export: &BoneCustomPropExport) -> Vec<String> {
98 let mut names: std::collections::HashSet<String> = std::collections::HashSet::new();
99 for p in &export.props {
100 names.insert(p.bone_name.clone());
101 }
102 let mut v: Vec<_> = names.into_iter().collect();
103 v.sort();
104 v
105}
106
107#[allow(dead_code)]
109pub fn bone_custom_prop_to_json(export: &BoneCustomPropExport) -> String {
110 format!(r#"{{"custom_props":{}}}"#, export.props.len())
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn add_float_and_count() {
119 let mut e = new_bone_custom_prop_export();
120 add_float_prop(&mut e, "spine", "stiffness", 0.5);
121 assert_eq!(prop_count(&e), 1);
122 }
123
124 #[test]
125 fn find_prop_found() {
126 let mut e = new_bone_custom_prop_export();
127 add_float_prop(&mut e, "arm", "twist", 0.3);
128 assert!(find_prop(&e, "arm", "twist").is_some());
129 }
130
131 #[test]
132 fn find_prop_missing() {
133 let e = new_bone_custom_prop_export();
134 assert!(find_prop(&e, "arm", "twist").is_none());
135 }
136
137 #[test]
138 fn props_for_bone_filtered() {
139 let mut e = new_bone_custom_prop_export();
140 add_float_prop(&mut e, "arm", "a", 1.0);
141 add_float_prop(&mut e, "leg", "b", 2.0);
142 assert_eq!(props_for_bone(&e, "arm").len(), 1);
143 }
144
145 #[test]
146 fn bone_names_unique_sorted() {
147 let mut e = new_bone_custom_prop_export();
148 add_float_prop(&mut e, "b", "x", 1.0);
149 add_float_prop(&mut e, "a", "y", 1.0);
150 let names = bone_names_with_props(&e);
151 assert_eq!(names[0], "a");
152 }
153
154 #[test]
155 fn add_bool_prop_ok() {
156 let mut e = new_bone_custom_prop_export();
157 add_bool_prop(&mut e, "head", "ik_enabled", true);
158 assert_eq!(prop_count(&e), 1);
159 }
160
161 #[test]
162 fn add_text_prop_ok() {
163 let mut e = new_bone_custom_prop_export();
164 add_text_prop(&mut e, "root", "group", "physics");
165 assert!(find_prop(&e, "root", "group").is_some());
166 }
167
168 #[test]
169 fn json_has_count() {
170 let mut e = new_bone_custom_prop_export();
171 add_float_prop(&mut e, "x", "y", 1.0);
172 let j = bone_custom_prop_to_json(&e);
173 assert!(j.contains("\"custom_props\":1"));
174 }
175
176 #[test]
177 fn empty_export() {
178 let e = new_bone_custom_prop_export();
179 assert_eq!(prop_count(&e), 0);
180 }
181
182 #[test]
183 fn multiple_props_same_bone() {
184 let mut e = new_bone_custom_prop_export();
185 add_float_prop(&mut e, "arm", "a", 1.0);
186 add_float_prop(&mut e, "arm", "b", 2.0);
187 assert_eq!(props_for_bone(&e, "arm").len(), 2);
188 }
189}