oxihuman_export/
cloth_sim_state_export.rs1#![allow(dead_code)]
4
5#[allow(dead_code)]
7#[derive(Clone)]
8pub struct ClothParticleState {
9 pub position: [f32; 3],
10 pub velocity: [f32; 3],
11 pub pinned: bool,
12}
13
14#[allow(dead_code)]
16pub struct ClothSimStateExport {
17 pub frame: usize,
18 pub time: f32,
19 pub particles: Vec<ClothParticleState>,
20}
21
22#[allow(dead_code)]
24pub fn new_cloth_sim_state(frame: usize, time: f32) -> ClothSimStateExport {
25 ClothSimStateExport {
26 frame,
27 time,
28 particles: Vec::new(),
29 }
30}
31
32#[allow(dead_code)]
34pub fn add_cloth_particle_state(
35 export: &mut ClothSimStateExport,
36 pos: [f32; 3],
37 vel: [f32; 3],
38 pinned: bool,
39) {
40 export.particles.push(ClothParticleState {
41 position: pos,
42 velocity: vel,
43 pinned,
44 });
45}
46
47#[allow(dead_code)]
49pub fn particle_count_css(export: &ClothSimStateExport) -> usize {
50 export.particles.len()
51}
52
53#[allow(dead_code)]
55pub fn pinned_count_css(export: &ClothSimStateExport) -> usize {
56 export.particles.iter().filter(|p| p.pinned).count()
57}
58
59#[allow(dead_code)]
61pub fn avg_speed_css(export: &ClothSimStateExport) -> f32 {
62 if export.particles.is_empty() {
63 return 0.0;
64 }
65 let sum: f32 = export
66 .particles
67 .iter()
68 .map(|p| {
69 let v = p.velocity;
70 (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt()
71 })
72 .sum();
73 sum / export.particles.len() as f32
74}
75
76#[allow(dead_code)]
78pub fn max_speed_css(export: &ClothSimStateExport) -> f32 {
79 export
80 .particles
81 .iter()
82 .map(|p| {
83 let v = p.velocity;
84 (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt()
85 })
86 .fold(0.0_f32, f32::max)
87}
88
89#[allow(dead_code)]
91pub fn flat_positions_css(export: &ClothSimStateExport) -> Vec<f32> {
92 export.particles.iter().flat_map(|p| p.position).collect()
93}
94
95#[allow(dead_code)]
97pub fn cloth_sim_state_to_json(export: &ClothSimStateExport) -> String {
98 format!(
99 r#"{{"frame":{},"time":{:.4},"particles":{},"pinned":{}}}"#,
100 export.frame,
101 export.time,
102 export.particles.len(),
103 pinned_count_css(export)
104 )
105}
106
107#[allow(dead_code)]
109pub fn validate_cloth_sim_state(export: &ClothSimStateExport, expected: usize) -> bool {
110 export.particles.len() == expected
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn add_and_count() {
119 let mut e = new_cloth_sim_state(0, 0.0);
120 add_cloth_particle_state(&mut e, [0.0; 3], [0.0; 3], false);
121 assert_eq!(particle_count_css(&e), 1);
122 }
123
124 #[test]
125 fn pinned_count() {
126 let mut e = new_cloth_sim_state(0, 0.0);
127 add_cloth_particle_state(&mut e, [0.0; 3], [0.0; 3], true);
128 add_cloth_particle_state(&mut e, [0.0; 3], [0.0; 3], false);
129 assert_eq!(pinned_count_css(&e), 1);
130 }
131
132 #[test]
133 fn avg_speed_zero() {
134 let mut e = new_cloth_sim_state(0, 0.0);
135 add_cloth_particle_state(&mut e, [0.0; 3], [0.0; 3], false);
136 assert!((avg_speed_css(&e) - 0.0).abs() < 1e-6);
137 }
138
139 #[test]
140 fn avg_speed_known() {
141 let mut e = new_cloth_sim_state(0, 0.0);
142 add_cloth_particle_state(&mut e, [0.0; 3], [3.0, 4.0, 0.0], false);
143 assert!((avg_speed_css(&e) - 5.0).abs() < 1e-5);
144 }
145
146 #[test]
147 fn max_speed() {
148 let mut e = new_cloth_sim_state(0, 0.0);
149 add_cloth_particle_state(&mut e, [0.0; 3], [1.0, 0.0, 0.0], false);
150 add_cloth_particle_state(&mut e, [0.0; 3], [3.0, 4.0, 0.0], false);
151 assert!((max_speed_css(&e) - 5.0).abs() < 1e-5);
152 }
153
154 #[test]
155 fn flat_positions_size() {
156 let mut e = new_cloth_sim_state(0, 0.0);
157 add_cloth_particle_state(&mut e, [1.0, 2.0, 3.0], [0.0; 3], false);
158 assert_eq!(flat_positions_css(&e).len(), 3);
159 }
160
161 #[test]
162 fn json_has_frame() {
163 let e = new_cloth_sim_state(5, 0.1);
164 let j = cloth_sim_state_to_json(&e);
165 assert!(j.contains("\"frame\":5"));
166 }
167
168 #[test]
169 fn validate_count() {
170 let mut e = new_cloth_sim_state(0, 0.0);
171 add_cloth_particle_state(&mut e, [0.0; 3], [0.0; 3], false);
172 assert!(validate_cloth_sim_state(&e, 1));
173 }
174
175 #[test]
176 fn empty_avg_speed() {
177 let e = new_cloth_sim_state(0, 0.0);
178 assert!((avg_speed_css(&e) - 0.0).abs() < 1e-6);
179 }
180
181 #[test]
182 fn time_stored() {
183 let e = new_cloth_sim_state(0, 1.5);
184 assert!((e.time - 1.5).abs() < 1e-4);
185 }
186}