#![allow(dead_code)]
pub fn build_adjacency(indices: &[u32], vertex_count: usize) -> Vec<Vec<usize>> {
let mut adj: Vec<Vec<usize>> = vec![Vec::new(); vertex_count];
let face_count = indices.len() / 3;
for fi in 0..face_count {
let a = indices[fi * 3] as usize;
let b = indices[fi * 3 + 1] as usize;
let c = indices[fi * 3 + 2] as usize;
for (x, y) in [(a, b), (b, c), (c, a), (b, a), (c, b), (a, c)] {
if !adj[x].contains(&y) {
adj[x].push(y);
}
}
}
adj
}
pub fn smooth_step(positions: &[[f32; 3]], adj: &[Vec<usize>], factor: f32) -> Vec<[f32; 3]> {
let n = positions.len();
let mut result = positions.to_vec();
for i in 0..n {
let neighbors = &adj[i];
if neighbors.is_empty() {
continue;
}
let mut cx = 0.0_f32;
let mut cy = 0.0_f32;
let mut cz = 0.0_f32;
for &j in neighbors {
cx += positions[j][0];
cy += positions[j][1];
cz += positions[j][2];
}
let nf = neighbors.len() as f32;
cx /= nf;
cy /= nf;
cz /= nf;
result[i][0] = positions[i][0] + (cx - positions[i][0]) * factor;
result[i][1] = positions[i][1] + (cy - positions[i][1]) * factor;
result[i][2] = positions[i][2] + (cz - positions[i][2]) * factor;
}
result
}
pub fn smooth_n(
positions: &[[f32; 3]],
indices: &[u32],
factor: f32,
iterations: usize,
) -> Vec<[f32; 3]> {
let adj = build_adjacency(indices, positions.len());
let mut pos = positions.to_vec();
for _ in 0..iterations {
pos = smooth_step(&pos, &adj, factor);
}
pos
}
pub fn smooth_displacement(original: &[[f32; 3]], smoothed: &[[f32; 3]]) -> Vec<f32> {
let n = original.len().min(smoothed.len());
(0..n)
.map(|i| {
let dx = smoothed[i][0] - original[i][0];
let dy = smoothed[i][1] - original[i][1];
let dz = smoothed[i][2] - original[i][2];
(dx * dx + dy * dy + dz * dz).sqrt()
})
.collect()
}
pub fn max_displacement(disp: &[f32]) -> f32 {
disp.iter().cloned().fold(0.0_f32, f32::max)
}
#[cfg(test)]
mod tests {
use super::*;
fn line_mesh() -> (Vec<[f32; 3]>, Vec<u32>) {
let pos = vec![[0.0_f32, 0.0, 0.0], [1.0, 0.0, 0.0], [2.0, 0.0, 0.0]];
let idx = vec![0u32, 1, 2];
(pos, idx)
}
#[test]
fn test_build_adjacency_size() {
let (_, idx) = line_mesh();
let adj = build_adjacency(&idx, 3);
assert_eq!(adj.len(), 3);
}
#[test]
fn test_smooth_step_output_size() {
let (pos, idx) = line_mesh();
let adj = build_adjacency(&idx, 3);
let result = smooth_step(&pos, &adj, 0.5);
assert_eq!(result.len(), 3);
}
#[test]
fn test_smooth_n_zero_iterations() {
let (pos, idx) = line_mesh();
let result = smooth_n(&pos, &idx, 0.5, 0);
assert!((result[1][0] - 1.0).abs() < 1e-6);
}
#[test]
fn test_smooth_n_reduces_extremes() {
let (pos, idx) = line_mesh();
let result = smooth_n(&pos, &idx, 1.0, 5);
assert!(result[0][0] > 0.0 || result[0][0] >= 0.0);
}
#[test]
fn test_smooth_displacement_zero_no_change() {
let (pos, idx) = line_mesh();
let result = smooth_n(&pos, &idx, 0.0, 1);
let disp = smooth_displacement(&pos, &result);
assert!(disp.iter().all(|&d| d < 1e-6));
}
#[test]
fn test_smooth_displacement_nonzero() {
let (pos, idx) = line_mesh();
let result = smooth_n(&pos, &idx, 0.5, 1);
let disp = smooth_displacement(&pos, &result);
assert!(disp.iter().any(|&d| d > 0.0));
}
#[test]
fn test_max_displacement() {
let disp = vec![0.1_f32, 0.5, 0.3];
assert!((max_displacement(&disp) - 0.5).abs() < 1e-6);
}
#[test]
fn test_max_displacement_empty() {
assert_eq!(max_displacement(&[]), 0.0);
}
}