use std::{
fs::File,
io::{self, BufRead, BufReader, BufWriter, Write},
path::Path,
};
use crate::{
geometry::{
point::{Point, PointOps},
spatial_element::SpatialElement,
vector::{Vector, VectorOps},
},
mesh::basic_types::Mesh,
numeric::scalar::Scalar,
};
pub fn write_off<T: Scalar, const N: usize, P: AsRef<Path>>(
mesh: &Mesh<T, N>,
path: P,
) -> io::Result<()>
where
Point<T, N>: PointOps<T, N, Vector = Vector<T, N>>,
Vector<T, N>: VectorOps<T, N, Cross = Vector<T, N>>,
for<'a> &'a T: std::ops::Sub<&'a T, Output = T>
+ std::ops::Mul<&'a T, Output = T>
+ std::ops::Add<&'a T, Output = T>
+ std::ops::Div<&'a T, Output = T>
+ std::ops::Neg<Output = T>,
{
let file = File::create(path)?;
let mut out = BufWriter::new(file);
let face_count = mesh.faces.iter().filter(|f| !f.removed).count();
let vert_count = mesh.vertices.len();
writeln!(out, "OFF")?;
writeln!(out, "{} {} {}", vert_count, face_count, 0)?;
for v in &mesh.vertices {
let c = v.position.coords();
writeln!(
out,
"{} {} {}",
c[0].to_f64().unwrap(),
c[1].to_f64().unwrap(),
c[2].to_f64().unwrap()
)?;
}
for f in 0..mesh.faces.len() {
if mesh.faces[f].removed {
continue;
}
let vs = mesh.face_vertices(f);
writeln!(out, "3 {} {} {}", vs[0], vs[1], vs[2])?;
}
out.flush()
}
pub fn read_off<T: Scalar, P: AsRef<Path>>(path: P) -> io::Result<Mesh<T, 3>>
where
Point<T, 3>: PointOps<T, 3, Vector = Vector<T, 3>>,
Vector<T, 3>: VectorOps<T, 3, Cross = Vector<T, 3>>,
for<'a> &'a T: std::ops::Sub<&'a T, Output = T>
+ std::ops::Mul<&'a T, Output = T>
+ std::ops::Add<&'a T, Output = T>
+ std::ops::Div<&'a T, Output = T>
+ std::ops::Neg<Output = T>,
{
let file = File::open(path)?;
let reader = BufReader::new(file);
fn tokenize<R: BufRead>(r: R) -> io::Result<Vec<String>> {
let mut toks = Vec::new();
for line in r.lines() {
let l = line?;
let trimmed = l.split('#').next().unwrap_or("").trim();
if trimmed.is_empty() {
continue;
}
toks.extend(trimmed.split_whitespace().map(|s| s.to_string()));
}
Ok(toks)
}
let toks = tokenize(reader)?;
if toks.is_empty() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"OFF: empty file",
));
}
let mut it = toks.into_iter();
let header = it
.next()
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "OFF: missing header"))?;
if header != "OFF" {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("OFF: expected 'OFF', got '{}'", header),
));
}
let vcount: usize = it
.next()
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "OFF: missing vertex count"))?
.parse()
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "OFF: bad vertex count"))?;
let fcount: usize = it
.next()
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "OFF: missing face count"))?
.parse()
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "OFF: bad face count"))?;
let _ecount: usize = it
.next()
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "OFF: missing edge count"))?
.parse()
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "OFF: bad edge count"))?;
let mut mesh = Mesh::new();
for _ in 0..vcount {
let x: f64 = it
.next()
.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, "OFF: incomplete vertex (x)")
})?
.parse()
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "OFF: bad vertex x"))?;
let y: f64 = it
.next()
.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, "OFF: incomplete vertex (y)")
})?
.parse()
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "OFF: bad vertex y"))?;
let z: f64 = it
.next()
.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, "OFF: incomplete vertex (z)")
})?
.parse()
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "OFF: bad vertex z"))?;
let _vid = mesh.add_vertex(Point::<T, 3>::from_vals([x, y, z]));
}
for _ in 0..fcount {
let poly_size: usize = it
.next()
.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, "OFF: face missing vertex count")
})?
.parse()
.map_err(|_| {
io::Error::new(io::ErrorKind::InvalidData, "OFF: bad face vertex count")
})?;
if poly_size == 3 {
let a: usize = it
.next()
.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, "OFF: face missing index a")
})?
.parse()
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "OFF: bad index a"))?;
let b: usize = it
.next()
.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, "OFF: face missing index b")
})?
.parse()
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "OFF: bad index b"))?;
let c: usize = it
.next()
.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, "OFF: face missing index c")
})?
.parse()
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "OFF: bad index c"))?;
mesh.add_triangle(a, b, c);
} else {
for _ in 0..poly_size {
let _ = it.next(); }
}
}
Ok(mesh)
}