use std::collections::BTreeSet;
use axiolid_core::{Point3, Scalar};
use axiolid_mesh::{EdgeAdjacency, TriMesh};
use crate::{RefineError, SmoothReport};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SmoothOptions {
pub factor: Scalar,
pub passes: u32,
pub fix_boundary: bool,
}
impl Default for SmoothOptions {
fn default() -> Self {
Self {
factor: 0.5,
passes: 1,
fix_boundary: true,
}
}
}
pub fn smooth(
mesh: &TriMesh,
options: SmoothOptions,
) -> Result<(TriMesh, SmoothReport), RefineError> {
if mesh.indices.len() % 3 != 0 {
return Err(RefineError::RaggedIndices(mesh.indices.len()));
}
let vertex_count = mesh.positions.len();
for (triangle, chunk) in mesh.indices.chunks_exact(3).enumerate() {
for &index in chunk {
if index as usize >= vertex_count {
return Err(RefineError::IndexOutOfRange(triangle, index));
}
}
}
if !(options.factor > 0.0 && options.factor <= 1.0) {
return Err(RefineError::InvalidTarget(options.factor));
}
let adjacency = EdgeAdjacency::build(mesh);
let boundary: BTreeSet<u32> = adjacency.boundary_vertices().into_iter().collect();
let neighbours = adjacency.vertex_neighbours();
let mut positions = mesh.positions.clone();
let mut moved = 0usize;
let mut max_movement: Scalar = 0.0;
for _ in 0..options.passes {
let source = positions.clone();
for (index, position) in positions.iter_mut().enumerate() {
let vertex = index as u32;
if options.fix_boundary && boundary.contains(&vertex) {
continue;
}
let adjacent = &neighbours[index];
if adjacent.is_empty() {
continue;
}
let mut sum = Point3::ZERO;
for &other in adjacent {
sum += source[other as usize];
}
let average = sum / (adjacent.len() as Scalar);
let target = source[index] + (average - source[index]) * options.factor;
let movement = (target - source[index]).length();
if movement > 0.0 {
moved += 1;
max_movement = max_movement.max(movement);
}
*position = target;
}
}
let attribute_fates = crate::carry_attributes(mesh);
let out = TriMesh {
positions,
indices: mesh.indices.clone(),
normals: None,
attributes: Vec::new(),
};
let report = SmoothReport {
vertices_moved: moved,
boundary_vertices: boundary.len(),
max_movement,
attribute_fates,
};
Ok((out, report))
}