diff --git a/Cargo.lock b/Cargo.lock index f9e38d25b..58320c68d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -733,10 +733,10 @@ dependencies = [ name = "fj-export" version = "0.6.0" dependencies = [ - "anyhow", "fj-interop", "fj-math", "stl", + "thiserror", "threemf", ] diff --git a/crates/fj-export/Cargo.toml b/crates/fj-export/Cargo.toml index dd85e88c9..66202bfe4 100644 --- a/crates/fj-export/Cargo.toml +++ b/crates/fj-export/Cargo.toml @@ -13,7 +13,7 @@ categories = ["encoding", "mathematics", "rendering"] [dependencies] -anyhow = "1.0.57" +thiserror = "1.0.31" threemf = "0.3.0" stl = "0.2.1" diff --git a/crates/fj-export/src/lib.rs b/crates/fj-export/src/lib.rs index 130fb0a2a..599d84089 100644 --- a/crates/fj-export/src/lib.rs +++ b/crates/fj-export/src/lib.rs @@ -16,10 +16,10 @@ use std::{fs::File, path::Path}; -use anyhow::{anyhow, Result}; +use thiserror::Error; use fj_interop::mesh::Mesh; -use fj_math::{Point, Triangle, Vector}; +use fj_math::{Point, Triangle}; /// Export the provided mesh to the file at the given path. /// @@ -27,7 +27,7 @@ use fj_math::{Point, Triangle, Vector}; /// /// Currently 3MF & STL file types are supported. The case insensitive file extension of /// the provided path is used to switch between supported types. -pub fn export(mesh: &Mesh>, path: &Path) -> Result<()> { +pub fn export(mesh: &Mesh>, path: &Path) -> Result<(), Error> { match path.extension() { Some(extension) if extension.to_ascii_uppercase() == "3MF" => { export_3mf(mesh, path) @@ -35,14 +35,14 @@ pub fn export(mesh: &Mesh>, path: &Path) -> Result<()> { Some(extension) if extension.to_ascii_uppercase() == "STL" => { export_stl(mesh, path) } - Some(extension) => { - Err(anyhow!("Extension not recognised, got {:?}", extension)) - } - None => Err(anyhow!("No extension specified")), + Some(extension) => Err(Error::InvalidExtension( + extension.to_str().map(|s| s.to_string()), + )), + None => Err(Error::NoExtension), } } -fn export_3mf(mesh: &Mesh>, path: &Path) -> Result<()> { +fn export_3mf(mesh: &Mesh>, path: &Path) -> Result<(), Error> { let vertices = mesh.vertices().map(|vertex| vertex.into()).collect(); let indices: Vec<_> = mesh.indices().collect(); @@ -67,34 +67,21 @@ fn export_3mf(mesh: &Mesh>, path: &Path) -> Result<()> { Ok(()) } -fn export_stl(mesh: &Mesh>, path: &Path) -> Result<()> { +fn export_stl(mesh: &Mesh>, path: &Path) -> Result<(), Error> { let points = mesh .triangles() .map(|triangle| triangle.points) .collect::>(); let vertices = points.iter().map(|points| { - points.map(|point| { - [point.x.into_f32(), point.y.into_f32(), point.z.into_f32()] - }) + points.map(|point| point.coords.components.map(|s| s.into_f32())) }); let normals = points .iter() .map(|&points| points.into()) - .map(|triangle: Triangle<3>| triangle.to_parry().normal()) - .collect::>>() - .ok_or_else(|| anyhow!("Unable to compute normal"))?; - - let normals = normals.iter().map(|vector| vector.into_inner().into()).map( - |vector: Vector<3>| { - [ - vector.x.into_f32(), - vector.y.into_f32(), - vector.z.into_f32(), - ] - }, - ); + .map(|triangle: Triangle<3>| triangle.normal()) + .map(|vector| vector.components.map(|s| s.into_f32())); let triangles = vertices .zip(normals) @@ -112,7 +99,10 @@ fn export_stl(mesh: &Mesh>, path: &Path) -> Result<()> { let binary_stl_file = stl::BinaryStlFile { header: stl::BinaryStlHeader { header: [0u8; 80], - num_triangles: triangles.len().try_into()?, + num_triangles: triangles + .len() + .try_into() + .map_err(|_| Error::InvalidTriangleCount)?, }, triangles, }; @@ -121,3 +111,27 @@ fn export_stl(mesh: &Mesh>, path: &Path) -> Result<()> { Ok(()) } + +/// An error that can occur while exporting +#[derive(Debug, Error)] +pub enum Error { + /// No extension specified + #[error("no extension specified")] + NoExtension, + + /// Unrecognised extension found `{0:?}` + #[error("unrecognised extension found `{0:?}`")] + InvalidExtension(Option), + + /// I/O error whilst exporting to file + #[error("I/O error whilst exporting to file")] + Io(#[from] std::io::Error), + + /// Maximum triangle count exceeded + #[error("maximum triangle count exceeded")] + InvalidTriangleCount, + + /// Threemf error whilst exporting to 3MF file + #[error("threemf error whilst exporting to 3MF file")] + ThreeMF(#[from] threemf::Error), +}