use std::collections::HashMap;
use std::io::Result;
#[derive(Default, Clone, Copy, Debug, PartialEq, PartialOrd)]
pub struct Vector<F>(pub [F; 3]);
impl<F> Vector<F> {
pub const fn new(v: [F; 3]) -> Self {
Self(v)
}
}
impl<F> From<Vector<F>> for [F; 3] {
fn from(v: Vector<F>) -> Self {
v.0
}
}
impl<F> std::ops::Index<usize> for Vector<F> {
type Output = F;
fn index(&self, i: usize) -> &Self::Output {
assert!(i < 3);
&self.0[i]
}
}
pub type Vertex = Vector<f32>;
pub type Normal = Vector<f32>;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Triangle {
pub normal: Normal,
pub vertices: [Vertex; 3],
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct IndexedTriangle {
pub normal: Normal,
pub vertices: [usize; 3],
}
#[derive(Clone, Debug, PartialEq)]
pub struct IndexedMesh {
pub vertices: Vec<Vertex>,
pub faces: Vec<IndexedTriangle>,
}
impl IndexedMesh {
pub fn validate(&self) -> Result<()> {
let mut unconnected_edges: HashMap<(usize, usize), (usize, usize, usize)> = HashMap::new();
for (fi, face) in self.faces.iter().enumerate() {
{
let a = self.vertices[face.vertices[0]];
let b = self.vertices[face.vertices[1]];
let c = self.vertices[face.vertices[2]];
let area = super::utils::tri_area(a, b, c);
if area < f32::EPSILON {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("face #{fi} has a zero-area face"),
));
}
}
for i in 0..3 {
let u = face.vertices[i];
let v = face.vertices[(i + 1) % 3];
if unconnected_edges.contains_key(&(v, u)) {
unconnected_edges.remove(&(v, u));
} else {
unconnected_edges.insert((u, v), (fi, i, (i + 1) % 3));
}
}
}
if let Some((fi, i1, i2)) = unconnected_edges.values().next() {
Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("did not find facing edge for face #{fi}, edge #v{i1} -> #v{i2}"),
))
} else {
Ok(())
}
}
pub fn into_triangle_vec(self) -> Vec<Triangle> {
self.faces
.iter()
.map(|a| Triangle {
normal: a.normal,
vertices: [
self.vertices[a.vertices[0]],
self.vertices[a.vertices[1]],
self.vertices[a.vertices[2]],
],
})
.collect()
}
}