use crate::*;
use std::{
fmt,
io::{BufRead, Error as ioError},
};
use super::{types::*, utils::*};
pub fn load_off_mesh<EM, P, R>(read: &mut R, mesh: &mut EM) -> OffResult<()>
where
EM: IsFaceEditableMesh<P, Face3> + IsVertexEditableMesh<P, Face3>,
P: IsBuildable3D + Clone,
R: BufRead,
{
let mut line_buffer = Vec::new();
let mut i_line = 0;
let mut off_seen = false;
let mut counts = None;
while let Ok(line) = fetch_line(read, &mut line_buffer) {
i_line += 1;
if !off_seen && line.starts_with(b"OFF") {
off_seen = true;
continue;
}
if line.is_empty() || line.starts_with(b"#") {
continue;
}
if counts.is_none() {
let mut words = to_words_skip_empty(line);
let n_vertices = words
.next()
.and_then(|word| from_ascii(word))
.ok_or(OffError::VertexCount)
.line(i_line, line)?;
let n_faces = words
.next()
.and_then(|word| from_ascii(word))
.ok_or(OffError::FaceCount)
.line(i_line, line)?;
mesh.reserve_vertices(n_vertices);
mesh.reserve_faces(n_faces);
counts = Some([n_vertices, n_faces]);
continue;
}
if mesh.num_vertices() < counts.unwrap()[0] {
let mut words = to_words_skip_empty(line);
let x = words
.next()
.and_then(|word| from_ascii(word))
.ok_or(OffError::Vertex)
.line(i_line, line)?;
let y = words
.next()
.and_then(|word| from_ascii(word))
.ok_or(OffError::Vertex)
.line(i_line, line)?;
let z = words
.next()
.and_then(|word| from_ascii(word))
.ok_or(OffError::Vertex)
.line(i_line, line)?;
mesh.add_vertex(P::new(x, y, z));
} else {
let mut words = to_words_skip_empty(line);
let count_face = words
.next()
.ok_or(OffError::FaceVertexCount)
.line(i_line, line)?;
if count_face == b"3" {
let a = words
.next()
.and_then(|word| from_ascii(word))
.ok_or(OffError::Face)
.line(i_line, line)?;
let b = words
.next()
.and_then(|word| from_ascii(word))
.ok_or(OffError::Face)
.line(i_line, line)?;
let c = words
.next()
.and_then(|word| from_ascii(word))
.ok_or(OffError::Face)
.line(i_line, line)?;
mesh.try_add_connection(VId { val: a }, VId { val: b }, VId { val: c })
.or(Err(OffError::InvalidMeshIndices).line(i_line, line))?;
}
}
}
Ok(())
}
pub fn load_off_points<IP, P, R>(read: &mut R, ip: &mut IP) -> OffResult<()>
where
IP: IsPushable<P>,
P: IsBuildable3D,
R: BufRead,
{
let mut line_buffer = Vec::new();
let mut i_line = 0;
let mut off_seen = false;
let mut n_vertices = None;
let mut n_added = 0;
while let Ok(line) = fetch_line(read, &mut line_buffer) {
i_line += 1;
if !off_seen && line.starts_with(b"OFF") {
off_seen = true;
continue;
}
if line.is_empty() || line.starts_with(b"#") {
continue;
}
if n_vertices.is_none() {
let mut words = to_words_skip_empty(line);
n_vertices = Some(
words
.next()
.and_then(|word| from_ascii(word))
.ok_or(OffError::VertexCount)
.line(i_line, line)?,
);
ip.reserve(n_vertices.unwrap());
continue;
}
if n_added < n_vertices.unwrap() {
let mut words = to_words_skip_empty(line);
let x = words
.next()
.and_then(|word| from_ascii(word))
.ok_or(OffError::Vertex)
.line(i_line, line)?;
let y = words
.next()
.and_then(|word| from_ascii(word))
.ok_or(OffError::Vertex)
.line(i_line, line)?;
let z = words
.next()
.and_then(|word| from_ascii(word))
.ok_or(OffError::Vertex)
.line(i_line, line)?;
ip.push(P::new(x, y, z));
n_added += 1;
} else {
break;
}
}
Ok(())
}
pub enum OffError {
AccessFile,
InvalidMeshIndices,
VertexCount,
FaceCount,
Vertex,
Face,
FaceVertexCount,
}
pub type OffResult<T> = IOResult<T, OffError>;
impl fmt::Debug for OffError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::AccessFile => write!(f, "Unable to access file"),
Self::VertexCount => write!(f, "Unable to parse vertex count"),
Self::FaceCount => write!(f, "Unable to parse face count"),
Self::Vertex => write!(f, "Unable to parse vertex"),
Self::Face => write!(f, "Unable to parse face"),
Self::FaceVertexCount => write!(f, "Unable to parse vertex count of face"),
Self::InvalidMeshIndices => write!(f, "File contains invalid mesh indices"),
}
}
}
impl fmt::Display for OffError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl From<ioError> for OffError {
fn from(_error: ioError) -> Self {
OffError::AccessFile
}
}