use nalgebra::Point3;
use parry3d_f64::shape::TriMesh;
use std::fs::File;
use std::io::{Cursor, Error, ErrorKind, Read};
use std::path::Path;
pub fn load_stl(path: &Path) -> Result<TriMesh, Error> {
let mut file = File::open(path)?;
let mut buffer = Vec::new();
file.read_to_end(&mut buffer)?;
if buffer.is_empty() {
return Err(Error::new(ErrorKind::InvalidData, "STL file is empty"));
}
let trimmed_start = buffer
.iter()
.position(|&c| !c.is_ascii_whitespace())
.unwrap_or(0);
let buffer = if trimmed_start > 0 {
buffer[trimmed_start..].to_vec()
} else {
buffer
};
let mut cursor = Cursor::new(buffer);
let stl = stl_io::read_stl(&mut cursor).map_err(|e| {
let error_msg = format!("{}", e);
if error_msg.contains("failed to fill whole buffer") {
Error::new(
ErrorKind::InvalidData,
format!(
"STL parse error: The file appears to be a binary STL with an incorrect triangle count in the header, \
or the file is truncated/corrupted. Original error: {}",
error_msg
),
)
} else {
Error::new(
ErrorKind::InvalidData,
format!("STL parse error: {}", error_msg),
)
}
})?;
if stl.vertices.is_empty() {
return Err(Error::new(
ErrorKind::InvalidData,
"No geometry data in STL file",
));
}
let vertices: Vec<Point3<f64>> = stl
.vertices
.iter()
.map(|v| Point3::new(v[0] as f64, v[1] as f64, v[2] as f64))
.collect();
let indices: Vec<[u32; 3]> = stl
.faces
.iter()
.map(|f| {
[
f.vertices[0] as u32,
f.vertices[1] as u32,
f.vertices[2] as u32,
]
})
.collect();
TriMesh::new(vertices, indices).map_err(|e| {
Error::new(
ErrorKind::InvalidData,
format!("Mesh creation error: {:?}", e),
)
})
}
pub fn load_vtk(_path: &Path) -> Result<TriMesh, Error> {
Err(Error::new(
ErrorKind::Unsupported,
"VTK loading not yet implemented. Use STL files.",
))
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn test_load_stl_box() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/data/box_10x10.stl");
if path.exists() {
let mesh = load_stl(&path).expect("Failed to load STL");
assert!(!mesh.vertices().is_empty());
assert!(!mesh.indices().is_empty());
}
}
}