#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Clone)]
pub struct ClothParticleState {
pub position: [f32; 3],
pub velocity: [f32; 3],
pub pinned: bool,
}
#[allow(dead_code)]
pub struct ClothSimStateExport {
pub frame: usize,
pub time: f32,
pub particles: Vec<ClothParticleState>,
}
#[allow(dead_code)]
pub fn new_cloth_sim_state(frame: usize, time: f32) -> ClothSimStateExport {
ClothSimStateExport {
frame,
time,
particles: Vec::new(),
}
}
#[allow(dead_code)]
pub fn add_cloth_particle_state(
export: &mut ClothSimStateExport,
pos: [f32; 3],
vel: [f32; 3],
pinned: bool,
) {
export.particles.push(ClothParticleState {
position: pos,
velocity: vel,
pinned,
});
}
#[allow(dead_code)]
pub fn particle_count_css(export: &ClothSimStateExport) -> usize {
export.particles.len()
}
#[allow(dead_code)]
pub fn pinned_count_css(export: &ClothSimStateExport) -> usize {
export.particles.iter().filter(|p| p.pinned).count()
}
#[allow(dead_code)]
pub fn avg_speed_css(export: &ClothSimStateExport) -> f32 {
if export.particles.is_empty() {
return 0.0;
}
let sum: f32 = export
.particles
.iter()
.map(|p| {
let v = p.velocity;
(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt()
})
.sum();
sum / export.particles.len() as f32
}
#[allow(dead_code)]
pub fn max_speed_css(export: &ClothSimStateExport) -> f32 {
export
.particles
.iter()
.map(|p| {
let v = p.velocity;
(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt()
})
.fold(0.0_f32, f32::max)
}
#[allow(dead_code)]
pub fn flat_positions_css(export: &ClothSimStateExport) -> Vec<f32> {
export.particles.iter().flat_map(|p| p.position).collect()
}
#[allow(dead_code)]
pub fn cloth_sim_state_to_json(export: &ClothSimStateExport) -> String {
format!(
r#"{{"frame":{},"time":{:.4},"particles":{},"pinned":{}}}"#,
export.frame,
export.time,
export.particles.len(),
pinned_count_css(export)
)
}
#[allow(dead_code)]
pub fn validate_cloth_sim_state(export: &ClothSimStateExport, expected: usize) -> bool {
export.particles.len() == expected
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn add_and_count() {
let mut e = new_cloth_sim_state(0, 0.0);
add_cloth_particle_state(&mut e, [0.0; 3], [0.0; 3], false);
assert_eq!(particle_count_css(&e), 1);
}
#[test]
fn pinned_count() {
let mut e = new_cloth_sim_state(0, 0.0);
add_cloth_particle_state(&mut e, [0.0; 3], [0.0; 3], true);
add_cloth_particle_state(&mut e, [0.0; 3], [0.0; 3], false);
assert_eq!(pinned_count_css(&e), 1);
}
#[test]
fn avg_speed_zero() {
let mut e = new_cloth_sim_state(0, 0.0);
add_cloth_particle_state(&mut e, [0.0; 3], [0.0; 3], false);
assert!((avg_speed_css(&e) - 0.0).abs() < 1e-6);
}
#[test]
fn avg_speed_known() {
let mut e = new_cloth_sim_state(0, 0.0);
add_cloth_particle_state(&mut e, [0.0; 3], [3.0, 4.0, 0.0], false);
assert!((avg_speed_css(&e) - 5.0).abs() < 1e-5);
}
#[test]
fn max_speed() {
let mut e = new_cloth_sim_state(0, 0.0);
add_cloth_particle_state(&mut e, [0.0; 3], [1.0, 0.0, 0.0], false);
add_cloth_particle_state(&mut e, [0.0; 3], [3.0, 4.0, 0.0], false);
assert!((max_speed_css(&e) - 5.0).abs() < 1e-5);
}
#[test]
fn flat_positions_size() {
let mut e = new_cloth_sim_state(0, 0.0);
add_cloth_particle_state(&mut e, [1.0, 2.0, 3.0], [0.0; 3], false);
assert_eq!(flat_positions_css(&e).len(), 3);
}
#[test]
fn json_has_frame() {
let e = new_cloth_sim_state(5, 0.1);
let j = cloth_sim_state_to_json(&e);
assert!(j.contains("\"frame\":5"));
}
#[test]
fn validate_count() {
let mut e = new_cloth_sim_state(0, 0.0);
add_cloth_particle_state(&mut e, [0.0; 3], [0.0; 3], false);
assert!(validate_cloth_sim_state(&e, 1));
}
#[test]
fn empty_avg_speed() {
let e = new_cloth_sim_state(0, 0.0);
assert!((avg_speed_css(&e) - 0.0).abs() < 1e-6);
}
#[test]
fn time_stored() {
let e = new_cloth_sim_state(0, 1.5);
assert!((e.time - 1.5).abs() < 1e-4);
}
}