#![allow(dead_code)]
#[derive(Debug, Clone)]
pub struct HairStrand {
pub root: usize,
pub points: Vec<[f32; 3]>,
}
impl HairStrand {
pub fn straight(
root_idx: usize,
root_pos: [f32; 3],
direction: [f32; 3],
length: f32,
segments: usize,
) -> Self {
let n = segments + 1;
let mut points = Vec::with_capacity(n);
for i in 0..n {
let t = if segments == 0 {
0.0
} else {
i as f32 / segments as f32
};
points.push([
root_pos[0] + direction[0] * length * t,
root_pos[1] + direction[1] * length * t,
root_pos[2] + direction[2] * length * t,
]);
}
Self {
root: root_idx,
points,
}
}
pub fn strand_length(&self) -> f32 {
let mut len = 0.0_f32;
for i in 1..self.points.len() {
let dx = self.points[i][0] - self.points[i - 1][0];
let dy = self.points[i][1] - self.points[i - 1][1];
let dz = self.points[i][2] - self.points[i - 1][2];
len += (dx * dx + dy * dy + dz * dz).sqrt();
}
len
}
pub fn segment_count(&self) -> usize {
self.points.len().saturating_sub(1)
}
}
#[derive(Debug, Clone)]
pub struct ParticleHairMesh {
pub strands: Vec<HairStrand>,
}
impl ParticleHairMesh {
pub fn new() -> Self {
Self {
strands: Vec::new(),
}
}
pub fn add_strand(&mut self, strand: HairStrand) {
self.strands.push(strand);
}
pub fn strand_count(&self) -> usize {
self.strands.len()
}
pub fn total_points(&self) -> usize {
self.strands.iter().map(|s| s.points.len()).sum()
}
}
impl Default for ParticleHairMesh {
fn default() -> Self {
Self::new()
}
}
pub fn average_strand_length(mesh: &ParticleHairMesh) -> f32 {
if mesh.strands.is_empty() {
return 0.0;
}
let sum: f32 = mesh.strands.iter().map(|s| s.strand_length()).sum();
sum / mesh.strands.len() as f32
}
pub fn max_strand_length(mesh: &ParticleHairMesh) -> f32 {
mesh.strands
.iter()
.map(|s| s.strand_length())
.fold(0.0_f32, f32::max)
}
#[cfg(test)]
mod tests {
use super::*;
fn straight_strand() -> HairStrand {
HairStrand::straight(0, [0.0, 0.0, 0.0], [0.0, 1.0, 0.0], 10.0, 5)
}
#[test]
fn test_strand_point_count() {
let s = straight_strand();
assert_eq!(s.points.len(), 6);
}
#[test]
fn test_strand_length() {
let s = straight_strand();
assert!((s.strand_length() - 10.0).abs() < 1e-4);
}
#[test]
fn test_segment_count() {
let s = straight_strand();
assert_eq!(s.segment_count(), 5);
}
#[test]
fn test_add_strand() {
let mut mesh = ParticleHairMesh::new();
mesh.add_strand(straight_strand());
mesh.add_strand(straight_strand());
assert_eq!(mesh.strand_count(), 2);
}
#[test]
fn test_total_points() {
let mut mesh = ParticleHairMesh::new();
mesh.add_strand(straight_strand());
mesh.add_strand(straight_strand());
assert_eq!(mesh.total_points(), 12);
}
#[test]
fn test_average_strand_length() {
let mut mesh = ParticleHairMesh::new();
mesh.add_strand(straight_strand());
let avg = average_strand_length(&mesh);
assert!((avg - 10.0).abs() < 1e-4);
}
#[test]
fn test_max_strand_length_empty() {
let mesh = ParticleHairMesh::new();
assert_eq!(max_strand_length(&mesh), 0.0);
}
#[test]
fn test_max_strand_length() {
let mut mesh = ParticleHairMesh::new();
mesh.add_strand(HairStrand::straight(0, [0.0; 3], [1.0, 0.0, 0.0], 5.0, 2));
mesh.add_strand(HairStrand::straight(1, [0.0; 3], [1.0, 0.0, 0.0], 20.0, 2));
assert!((max_strand_length(&mesh) - 20.0).abs() < 1e-4);
}
}