use std::path::Path;
pub use vtkio::Vtk;
use crate::mesh::attrib;
use crate::mesh::{PointCloud, PolyMesh, TetMesh};
pub mod obj;
pub mod vtk;
pub trait Real: vtkio::model::Scalar + crate::Real {}
impl<T> Real for T where T: vtkio::model::Scalar + crate::Real {}
const UV_ATTRIB_NAME: &str = "uv";
const NORMAL_ATTRIB_NAME: &str = "N";
pub trait MeshExtractor<T: crate::Real> {
fn extract_polymesh(&self) -> Result<PolyMesh<T>, Error> {
Err(Error::UnsupportedDataFormat)
}
fn extract_tetmesh(&self) -> Result<TetMesh<T>, Error> {
Err(Error::UnsupportedDataFormat)
}
fn extract_pointcloud(&self) -> Result<PointCloud<T>, Error> {
Err(Error::UnsupportedDataFormat)
}
}
pub fn load_tetmesh<T: Real, P: AsRef<Path>>(file: P) -> Result<TetMesh<T>, Error> {
load_tetmesh_impl(file.as_ref())
}
fn load_tetmesh_impl<T: Real>(file: &Path) -> Result<TetMesh<T>, Error> {
match file.extension().and_then(|ext| ext.to_str()) {
Some("vtk") | Some("vtu") | Some("pvtu") => {
let vtk = Vtk::import(file)?;
vtk.extract_tetmesh()
}
_ => Err(Error::UnsupportedFileFormat),
}
}
pub fn save_tetmesh<T: Real, P: AsRef<Path>>(tetmesh: &TetMesh<T>, file: P) -> Result<(), Error> {
save_tetmesh_impl(tetmesh, file.as_ref())
}
fn save_tetmesh_impl<T: Real>(tetmesh: &TetMesh<T>, file: &Path) -> Result<(), Error> {
match file.extension().and_then(|ext| ext.to_str()) {
Some("vtk") | Some("vtu") | Some("pvtu") => {
let vtk = vtk::convert_tetmesh_to_vtk_format(tetmesh)?;
vtk.export_be(file)?;
Ok(())
}
_ => Err(Error::UnsupportedFileFormat),
}
}
pub fn save_tetmesh_ascii<T: Real>(
tetmesh: &TetMesh<T>,
file: impl AsRef<Path>,
) -> Result<(), Error> {
save_tetmesh_ascii_impl(tetmesh, file.as_ref())
}
fn save_tetmesh_ascii_impl<T: Real>(tetmesh: &TetMesh<T>, file: &Path) -> Result<(), Error> {
match file.extension() {
Some(ext) if ext.to_str() == Some("vtk") => {
let vtk = vtk::convert_tetmesh_to_vtk_format(tetmesh)?;
vtk.export_ascii(file)?;
Ok(())
}
_ => Err(Error::UnsupportedFileFormat),
}
}
pub fn load_polymesh<T: Real, P: AsRef<Path>>(file: P) -> Result<PolyMesh<T>, Error> {
load_polymesh_impl(file.as_ref())
}
fn load_polymesh_impl<T: Real>(file: &Path) -> Result<PolyMesh<T>, Error> {
match file.extension().and_then(|ext| ext.to_str()) {
Some("vtk") | Some("vtu") | Some("vtp") | Some("pvtu") | Some("pvtp") => {
let vtk = Vtk::import(file)?;
vtk.extract_polymesh()
}
Some("obj") => {
let obj = obj::Obj::load_with_config(file, obj::LoadConfig { strict: false })?;
obj.data.extract_polymesh()
}
_ => Err(Error::UnsupportedFileFormat),
}
}
pub fn save_polymesh<T: Real, P: AsRef<Path>>(
polymesh: &PolyMesh<T>,
file: P,
) -> Result<(), Error> {
save_polymesh_impl(polymesh, file.as_ref())
}
fn save_polymesh_impl<T: Real>(polymesh: &PolyMesh<T>, file: &Path) -> Result<(), Error> {
match file.extension().and_then(|ext| ext.to_str()) {
Some("vtk") | Some("vtu") | Some("vtp") | Some("pvtu") | Some("pvtp") => {
let vtk =
vtk::convert_polymesh_to_vtk_format(polymesh, vtk::VTKPolyExportStyle::PolyData)?;
vtk.export_be(file)?;
Ok(())
}
Some("obj") => {
let obj = obj::convert_polymesh_to_obj_format(polymesh)?;
obj.save(file)?;
Ok(())
}
_ => Err(Error::UnsupportedFileFormat),
}
}
pub fn save_polymesh_ascii<T: Real, P: AsRef<Path>>(
polymesh: &PolyMesh<T>,
file: P,
) -> Result<(), Error> {
save_polymesh_ascii_impl(polymesh, file.as_ref())
}
fn save_polymesh_ascii_impl<T: Real>(polymesh: &PolyMesh<T>, file: &Path) -> Result<(), Error> {
match file.extension().and_then(|ext| ext.to_str()) {
Some("vtk") => {
let vtk =
vtk::convert_polymesh_to_vtk_format(polymesh, vtk::VTKPolyExportStyle::PolyData)?;
vtk.export_ascii(file)?;
Ok(())
}
Some("obj") => {
let obj = obj::convert_polymesh_to_obj_format(polymesh)?;
obj.save(file)?;
Ok(())
}
_ => Err(Error::UnsupportedFileFormat),
}
}
pub fn load_pointcloud<T: Real, P: AsRef<Path>>(file: P) -> Result<PointCloud<T>, Error> {
load_pointcloud_impl(file.as_ref())
}
fn load_pointcloud_impl<T: Real>(file: &Path) -> Result<PointCloud<T>, Error> {
match file.extension().and_then(|ext| ext.to_str()) {
Some("vtk") | Some("vtu") | Some("vtp") | Some("pvtu") | Some("pvtp") => {
let vtk = Vtk::import(file)?;
vtk.extract_pointcloud()
}
Some("obj") => {
let obj = obj::Obj::load_with_config(file, obj::LoadConfig { strict: false })?;
obj.data.extract_pointcloud()
}
_ => Err(Error::UnsupportedFileFormat),
}
}
pub fn save_pointcloud<T: Real, P: AsRef<Path>>(
ptcloud: &PointCloud<T>,
file: P,
) -> Result<(), Error> {
save_pointcloud_impl(ptcloud, file.as_ref())
}
pub fn save_pointcloud_impl<T: Real>(ptcloud: &PointCloud<T>, file: &Path) -> Result<(), Error> {
match file.extension().and_then(|ext| ext.to_str()) {
Some("vtk") | Some("vtu") | Some("vtp") | Some("pvtu") | Some("pvtp") => {
let vtk =
vtk::convert_pointcloud_to_vtk_format(ptcloud, vtk::VTKPolyExportStyle::PolyData)?;
vtk.export_be(file)?;
Ok(())
}
Some("obj") => {
let obj = obj::convert_pointcloud_to_obj_format(ptcloud)?;
obj.save(file)?;
Ok(())
}
_ => Err(Error::UnsupportedFileFormat),
}
}
pub fn save_pointcloud_ascii<T: Real, P: AsRef<Path>>(
ptcloud: &PointCloud<T>,
file: P,
) -> Result<(), Error> {
save_pointcloud_ascii_impl(ptcloud, file.as_ref())
}
fn save_pointcloud_ascii_impl<T: Real>(ptcloud: &PointCloud<T>, file: &Path) -> Result<(), Error> {
match file.extension().and_then(|ext| ext.to_str()) {
Some("vtk") => {
let vtk =
vtk::convert_pointcloud_to_vtk_format(ptcloud, vtk::VTKPolyExportStyle::PolyData)?;
vtk.export_ascii(file)?;
Ok(())
}
Some("obj") => {
let obj = obj::convert_pointcloud_to_obj_format(ptcloud)?;
obj.save(file)?;
Ok(())
}
_ => Err(Error::UnsupportedFileFormat),
}
}
#[derive(Debug)]
pub enum MeshIOError {
Vtk { source: vtk::VtkError },
Obj { source: obj::ObjError },
}
impl std::error::Error for MeshIOError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
MeshIOError::Vtk { source } => Some(source),
MeshIOError::Obj { source } => Some(source),
}
}
}
impl std::fmt::Display for MeshIOError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
MeshIOError::Vtk { source } => write!(f, "A Vtk IO error occurred: {}", source),
MeshIOError::Obj { source } => write!(f, "An Obj IO error occurred: {}", source),
}
}
}
#[derive(Debug)]
pub enum Error {
IO { source: std::io::Error },
MeshIO { source: MeshIOError },
Attrib { source: attrib::Error },
UnsupportedFileFormat,
UnsupportedDataFormat,
MeshTypeMismatch,
MissingMeshData,
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::IO { source } => Some(source),
Error::MeshIO { source } => Some(source),
Error::Attrib { .. } => {
None }
_ => None,
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Error::IO { source } => write!(f, "IO Error: {}", source),
Error::MeshIO { source } => write!(f, "An error in mesh IO occurred: {}", source),
Error::Attrib { source } => write!(f, "An attribute error occurred: {}", source),
Error::UnsupportedFileFormat => write!(f, "Unsupported file format specified"),
Error::UnsupportedDataFormat => write!(f, "Unsupported data format specified"),
Error::MeshTypeMismatch => write!(f, "Mesh type doesn't match expected type"),
Error::MissingMeshData => write!(f, "Missing mesh data"),
}
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Error {
Error::IO { source: err }
}
}
impl From<obj::ObjError> for Error {
fn from(err: obj::ObjError) -> Error {
Error::MeshIO {
source: MeshIOError::Obj { source: err },
}
}
}
impl From<vtk::VtkError> for Error {
fn from(err: vtk::VtkError) -> Error {
Error::MeshIO {
source: MeshIOError::Vtk { source: err },
}
}
}
impl From<attrib::Error> for Error {
fn from(err: attrib::Error) -> Error {
Error::Attrib { source: err }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn polycube() -> Result<(), Error> {
let polymesh: PolyMesh<f32> = load_polymesh("assets/cube.obj")?;
save_polymesh(&polymesh, "tests/artifacts/cube.obj")?;
let reloaded_polymesh = load_polymesh("tests/artifacts/cube.obj")?;
assert_eq!(polymesh, reloaded_polymesh);
Ok(())
}
#[test]
fn polycube_convert() -> Result<(), Error> {
use crate::mesh::TriMesh;
let _: TriMesh<f32> = load_polymesh("assets/cube.obj")?.into();
Ok(())
}
#[test]
fn tet_vtu() -> Result<(), Error> {
use crate::mesh::TetMesh;
let mesh: TetMesh<f32> = load_tetmesh("assets/tet.vtu")?;
dbg!(&mesh);
Ok(())
}
#[test]
fn cloth_argus() -> Result<(), Error> {
let polymesh: PolyMesh<f32> = load_polymesh("assets/cloth_argus.obj")?;
dbg!(&polymesh);
Ok(())
}
#[test]
fn tet_vtk_as_polymesh_error() {
assert!(load_polymesh::<f64, _>("assets/tet.vtk").is_err());
}
#[test]
fn unstructured_data_polymesh_real_test() {
assert!(load_polymesh::<f64, _>("./assets/tube.vtk").is_ok());
}
}